add create user and login user parts
This commit is contained in:
@@ -1,103 +1,91 @@
|
||||
// app/utils/emailVerification.util.js
|
||||
|
||||
const crypto = require("crypto");
|
||||
|
||||
const { sendMail } = require("../utils/mail.util");
|
||||
const redis = require("../config/redisClient");
|
||||
|
||||
const EMAIL_VERIFICATION_TTL =
|
||||
Number(
|
||||
process.env.EMAIL_VERIFICATION_TTL_SECONDS
|
||||
) || 1800;
|
||||
Number(process.env.EMAIL_VERIFICATION_TTL_SECONDS) || 1800;
|
||||
|
||||
const generateEmailVerificationToken = () => {
|
||||
return crypto
|
||||
.randomBytes(32)
|
||||
.toString("hex");
|
||||
return crypto.randomBytes(32).toString("hex");
|
||||
};
|
||||
|
||||
const hashEmailVerificationToken = (token) => {
|
||||
return crypto
|
||||
.createHash("sha256")
|
||||
.update(token)
|
||||
.digest("hex");
|
||||
return crypto.createHash("sha256").update(token).digest("hex");
|
||||
};
|
||||
|
||||
const createEmailVerification = async (userId) => {
|
||||
|
||||
// 1. Generate raw token
|
||||
const token =
|
||||
generateEmailVerificationToken();
|
||||
|
||||
const token = generateEmailVerificationToken();
|
||||
|
||||
// 2. Hash token
|
||||
const tokenHash =
|
||||
hashEmailVerificationToken(token);
|
||||
|
||||
const tokenHash = hashEmailVerificationToken(token);
|
||||
|
||||
// 3. Create Redis key
|
||||
const redisKey =
|
||||
`email-verification:${tokenHash}`;
|
||||
const redisKey = `email-verification:${tokenHash}`;
|
||||
|
||||
await redis.set(
|
||||
redisKey,
|
||||
userId,
|
||||
"EX",
|
||||
EMAIL_VERIFICATION_TTL
|
||||
);
|
||||
await redis.set(redisKey, userId, "EX", EMAIL_VERIFICATION_TTL);
|
||||
|
||||
return token;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Check a verification token.
|
||||
*/
|
||||
const verifyEmailVerificationToken =
|
||||
async (token) => {
|
||||
const verifyEmailVerificationToken = async (token) => {
|
||||
if (!token || typeof token !== "string") {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (
|
||||
!token ||
|
||||
typeof token !== "string"
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
// 1. Hash token received from user
|
||||
const tokenHash = hashEmailVerificationToken(token);
|
||||
|
||||
// 2. Build same Redis key
|
||||
const redisKey = `email-verification:${tokenHash}`;
|
||||
|
||||
// 1. Hash token received from user
|
||||
const tokenHash =
|
||||
hashEmailVerificationToken(token);
|
||||
// 3. Search Redis
|
||||
const userId = await redis.get(redisKey);
|
||||
|
||||
if (!userId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 2. Build same Redis key
|
||||
const redisKey =
|
||||
`email-verification:${tokenHash}`;
|
||||
|
||||
|
||||
// 3. Search Redis
|
||||
const userId =
|
||||
await redis.get(redisKey);
|
||||
|
||||
if (!userId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
userId,
|
||||
redisKey,
|
||||
};
|
||||
return {
|
||||
userId,
|
||||
redisKey,
|
||||
};
|
||||
};
|
||||
|
||||
const deleteEmailVerification =
|
||||
async (redisKey) => {
|
||||
//send verification email to user
|
||||
const sendVerificationEmail = async (email, firstName, verificationToken) => {
|
||||
const confirmationLink = `${process.env.FRONTEND_URL}/verify-email?token=${verificationToken}`;
|
||||
|
||||
await redis.del(redisKey);
|
||||
};
|
||||
// Send email
|
||||
await sendMail({
|
||||
to: email,
|
||||
|
||||
subject: "Confirm Your ZUMRI Account",
|
||||
|
||||
templateName: "emailVerification",
|
||||
|
||||
templateVars: {
|
||||
customer_name: firstName,
|
||||
confirmation_link: confirmationLink,
|
||||
},
|
||||
|
||||
text: `Hello ${firstName}, please verify your ZUMRI account using this link: ${confirmationLink}`,
|
||||
});
|
||||
};
|
||||
|
||||
const deleteEmailVerification = async (redisKey) => {
|
||||
await redis.del(redisKey);
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
createEmailVerification,
|
||||
verifyEmailVerificationToken,
|
||||
sendVerificationEmail,
|
||||
deleteEmailVerification,
|
||||
hashEmailVerificationToken,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -23,6 +23,14 @@ const generateUserId = () => {
|
||||
return "usr_" + Math.random().toString(36).slice(2, 10);
|
||||
};
|
||||
|
||||
const generateCustomerId = () => {
|
||||
return "cust_" + Math.random().toString(36).slice(2, 10);
|
||||
}
|
||||
|
||||
const generateBusinessCustomerId = () => {
|
||||
return "b_cust_" + Math.random().toString(36).slice(2, 10);
|
||||
}
|
||||
|
||||
const generateClientId = () => {
|
||||
const prefix = "cli_";
|
||||
return prefix + uuidv4();
|
||||
@@ -141,6 +149,8 @@ const generateDocumentReferenceNo = async (type) => {
|
||||
|
||||
module.exports = {
|
||||
generateUserId,
|
||||
generateCustomerId,
|
||||
generateBusinessCustomerId,
|
||||
generateClientId,
|
||||
generateInquId,
|
||||
generateInquRefNo,
|
||||
|
||||
+12
-1
@@ -15,6 +15,9 @@ require("dotenv").config();
|
||||
const JWT_SECRET = process.env.JWT_SECRET || "your_jwt_secret_key";
|
||||
const JWT_EXPIRES_IN = process.env.JWT_EXPIRES_IN || "1d"; // token validity
|
||||
|
||||
const REFRESH_TOKEN_SECRET = process.env.REFRESH_TOKEN_SECRET || "your_refresh_token_secret_key";
|
||||
const REFRESH_TOKEN_DAYS = process.env.REFRESH_TOKEN_DAYS || "7d"; // refresh token validity
|
||||
|
||||
/**
|
||||
* Generate JWT token
|
||||
* @param {Object} payload - usually { id, email, role }
|
||||
@@ -33,4 +36,12 @@ const verifyToken = (token) => {
|
||||
return jwt.verify(token, JWT_SECRET);
|
||||
};
|
||||
|
||||
module.exports = { generateToken, verifyToken };
|
||||
const generateRefreshToken = (payload) => {
|
||||
return jwt.sign(payload, REFRESH_TOKEN_SECRET, { expiresIn: REFRESH_TOKEN_DAYS });
|
||||
}
|
||||
|
||||
const verifyRefreshToken = (token) => {
|
||||
return jwt.verify(token, REFRESH_TOKEN_SECRET);
|
||||
}
|
||||
|
||||
module.exports = { generateToken, verifyToken, generateRefreshToken, verifyRefreshToken };
|
||||
|
||||
@@ -17,8 +17,9 @@ function generateOTP(key){
|
||||
return otp;
|
||||
}
|
||||
|
||||
function saveOTP(key, otp, ttl = 5 * 60 * 1000) { // default TTL: 5 mins
|
||||
const expiresAt = Date.now() + ttl;
|
||||
function saveOTP(key, otp) {
|
||||
ttl = parseInt(process.env.LOGIN_OTP_TTL_SECONDS || 300);
|
||||
const expiresAt = Date.now() + ttl * 1000; // Convert seconds to milliseconds
|
||||
otpCache.set(key, { otp, expiresAt });
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,201 @@
|
||||
// app/utils/refreshSession.util.js
|
||||
|
||||
const crypto = require("crypto");
|
||||
|
||||
const {
|
||||
generateRefreshToken,
|
||||
verifyRefreshToken,
|
||||
} = require("./jwt.util");
|
||||
|
||||
const refreshSessions = new Map();
|
||||
|
||||
|
||||
// Default = 7 days
|
||||
const REFRESH_SESSION_TTL =
|
||||
7 * 24 * 60 * 60 * 1000;
|
||||
|
||||
|
||||
const hashRefreshToken = (token) => {
|
||||
return crypto
|
||||
.createHash("sha256")
|
||||
.update(token)
|
||||
.digest("hex");
|
||||
};
|
||||
|
||||
const createRefreshSession = (userId) => {
|
||||
|
||||
// Unique session ID
|
||||
const sessionId =
|
||||
crypto.randomUUID();
|
||||
|
||||
|
||||
// Create raw Refresh JWT
|
||||
const refreshToken =
|
||||
generateRefreshToken({
|
||||
sub: userId,
|
||||
sid: sessionId,
|
||||
});
|
||||
|
||||
|
||||
// Hash raw refresh token
|
||||
const tokenHash =
|
||||
hashRefreshToken(
|
||||
refreshToken
|
||||
);
|
||||
|
||||
|
||||
// Expiration time
|
||||
const expiresAt =
|
||||
Date.now() +
|
||||
REFRESH_SESSION_TTL;
|
||||
|
||||
|
||||
// Save only HASH in RAM
|
||||
refreshSessions.set(
|
||||
sessionId,
|
||||
{
|
||||
userId,
|
||||
tokenHash,
|
||||
expiresAt,
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
return {
|
||||
refreshToken,
|
||||
sessionId,
|
||||
};
|
||||
};
|
||||
|
||||
const validateRefreshSession = (
|
||||
refreshToken
|
||||
) => {
|
||||
|
||||
if (!refreshToken) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
let decoded;
|
||||
|
||||
try {
|
||||
|
||||
decoded =
|
||||
verifyRefreshToken(
|
||||
refreshToken
|
||||
);
|
||||
|
||||
} catch (error) {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
|
||||
const sessionId =
|
||||
decoded.sid;
|
||||
|
||||
const userId =
|
||||
decoded.sub;
|
||||
|
||||
|
||||
if (!sessionId || !userId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
// Get session from RAM
|
||||
const session =
|
||||
refreshSessions.get(
|
||||
sessionId
|
||||
);
|
||||
|
||||
|
||||
if (!session) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
// Check session expiration
|
||||
if (
|
||||
Date.now() >
|
||||
session.expiresAt
|
||||
) {
|
||||
|
||||
refreshSessions.delete(
|
||||
sessionId
|
||||
);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
// Hash received refresh token
|
||||
const receivedHash =
|
||||
hashRefreshToken(
|
||||
refreshToken
|
||||
);
|
||||
|
||||
|
||||
// Compare stored hash
|
||||
if (
|
||||
receivedHash !==
|
||||
session.tokenHash
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
// Extra user check
|
||||
if (
|
||||
session.userId !==
|
||||
userId
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
userId,
|
||||
sessionId,
|
||||
};
|
||||
};
|
||||
|
||||
const deleteRefreshSession = (
|
||||
sessionId
|
||||
) => {
|
||||
|
||||
refreshSessions.delete(
|
||||
sessionId
|
||||
);
|
||||
};
|
||||
|
||||
const deleteAllUserSessions = (
|
||||
userId
|
||||
) => {
|
||||
|
||||
for (
|
||||
const [sessionId, session]
|
||||
of refreshSessions.entries()
|
||||
) {
|
||||
|
||||
if (
|
||||
session.userId === userId
|
||||
) {
|
||||
|
||||
refreshSessions.delete(
|
||||
sessionId
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
module.exports = {
|
||||
createRefreshSession,
|
||||
validateRefreshSession,
|
||||
deleteRefreshSession,
|
||||
deleteAllUserSessions,
|
||||
};
|
||||
@@ -0,0 +1,86 @@
|
||||
// app/utils/users/createBusinessCustomer.util.js
|
||||
|
||||
const db = require("../../models");
|
||||
|
||||
const {
|
||||
generateBusinessCustomerId,
|
||||
} = require("../idGen.util");
|
||||
|
||||
const BusinessCustomer = db.BusinessCustomer;
|
||||
|
||||
|
||||
/**
|
||||
* Create business-customer-specific details
|
||||
*/
|
||||
const createBusinessCustomerDetails = async (
|
||||
userId,
|
||||
businessData,
|
||||
transaction
|
||||
) => {
|
||||
|
||||
const {
|
||||
businessName,
|
||||
businessRegistrationNumber,
|
||||
businessType,
|
||||
contactName,
|
||||
phoneNumber,
|
||||
businessEmail,
|
||||
expectedMonthlyVolume,
|
||||
note,
|
||||
} = businessData;
|
||||
|
||||
|
||||
if (
|
||||
!businessName ||
|
||||
!businessRegistrationNumber ||
|
||||
!businessType ||
|
||||
!contactName ||
|
||||
!phoneNumber ||
|
||||
!businessEmail ||
|
||||
!expectedMonthlyVolume
|
||||
) {
|
||||
throw new Error(
|
||||
"Required business customer details are missing"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
const businessCustomer =
|
||||
await BusinessCustomer.create(
|
||||
{
|
||||
business_customer_id:
|
||||
generateBusinessCustomerId(),
|
||||
|
||||
user_id:
|
||||
userId,
|
||||
|
||||
businessName,
|
||||
|
||||
businessRegistrationNumber,
|
||||
|
||||
businessType,
|
||||
|
||||
contactName,
|
||||
|
||||
phoneNumber,
|
||||
|
||||
businessEmail,
|
||||
|
||||
expectedMonthlyVolume,
|
||||
|
||||
note:
|
||||
note || null,
|
||||
},
|
||||
{
|
||||
transaction,
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
return businessCustomer;
|
||||
};
|
||||
|
||||
|
||||
module.exports = {
|
||||
createBusinessCustomerDetails,
|
||||
};
|
||||
@@ -0,0 +1,54 @@
|
||||
//app/utils/users/createCustomerDetails.util.js
|
||||
|
||||
const db = require("../../models");
|
||||
|
||||
const { generateCustomerId } = require("../idGen.util");
|
||||
|
||||
const Customer = db.Customer;
|
||||
|
||||
const createCustomerDetails = async (
|
||||
userId,
|
||||
customerData,
|
||||
transaction
|
||||
) => {
|
||||
|
||||
const {
|
||||
address,
|
||||
phoneNumber,
|
||||
} = customerData;
|
||||
|
||||
|
||||
if (!address || !phoneNumber) {
|
||||
throw new Error(
|
||||
"Address and phone number are required for customer"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
const customer = await Customer.create(
|
||||
{
|
||||
customer_id:
|
||||
generateCustomerId(),
|
||||
|
||||
user_id:
|
||||
userId,
|
||||
|
||||
address:
|
||||
address,
|
||||
|
||||
phoneNumber:
|
||||
phoneNumber,
|
||||
},
|
||||
{
|
||||
transaction,
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
return customer;
|
||||
};
|
||||
|
||||
|
||||
module.exports = {
|
||||
createCustomerDetails,
|
||||
};
|
||||
Reference in New Issue
Block a user