Refactor registration handler to use a transaction for user creation and OTP generation; implement OTP storage and user role assignment

This commit is contained in:
paritosh18
2025-11-20 16:57:10 +05:30
parent f61287a11d
commit 00dd080ee0

View File

@@ -2,9 +2,12 @@ import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda
import { PrismaService } from '../../../common/database/prisma.service';
import { safeHandler } from '../../../common/utils/handlers/safeHandler';
import ApiError from '../../../common/utils/helper/ApiError';
import { generateOtpHelper } from '../../../common/utils/helper/sendOtp';
import * as bcrypt from 'bcryptjs';
import { OtpGeneratorSixDigit } from '../../../common/utils/helper/OtpGenerator';
import { encryptUserId } from '../../../common/utils/helper/CodeGenerator';
import { HostService } from '../services/host.service';
import { sendOtpEmailForHost } from '../services/sendOTPEmail.service';
import { ROLE } from '../../../common/utils/constants/common.constant';
const prismaService = new PrismaService();
const hostService = new HostService(prismaService);
@@ -28,7 +31,9 @@ export const handler = safeHandler(async (
throw new ApiError(400, 'Email is required');
}
const user = await prismaService.user.findUnique({
// Use a single transaction for user creation/lookup and OTP storage
const transactionResult = await prismaService.$transaction(async (tx) => {
const user = await tx.user.findUnique({
where: { emailAddress: email },
select: { emailAddress: true, id: true, userPassword: true },
});
@@ -37,29 +42,50 @@ export const handler = safeHandler(async (
throw new ApiError(409, 'User is already registered. Please login.');
}
let newUser;
let newUserLocal;
if (user && !user.userPassword) {
// ✅ User already exists but without password → reuse record
newUser = user;
// reuse existing invited user record
newUserLocal = user;
} else {
// ✅ No user found → create new one
newUser = await hostService.createMinglarUser(email);
// create new user record within the transaction
newUserLocal = await tx.user.create({
data: { emailAddress: email, roleXid: ROLE.HOST },
});
}
const otpResult = await generateOtpHelper(
Number(newUser?.id),
newUser?.emailAddress,
'Register',
6,
5
);
// Generate OTP (6-digit) and store within the same transaction
const otp = OtpGeneratorSixDigit.generateOtp();
const hashedOtp = await bcrypt.hash(otp, 10);
const expiry = new Date(Date.now() + 5 * 60 * 1000); // 5 minutes
if (!otpResult || !otpResult.otp) {
throw new ApiError(500, 'Failed to send OTP');
// delete old active OTPs for this user/purpose
await tx.userOtp.deleteMany({
where: { userXid: Number(newUserLocal.id), otpType: 'Register', isActive: true },
});
await tx.userOtp.create({
data: {
userXid: Number(newUserLocal.id),
otpType: 'Register',
otpCode: hashedOtp,
expiresOn: expiry,
isVerified: false,
isActive: true,
},
});
const encryptedId = encryptUserId(String(newUserLocal.id));
return { newUser: newUserLocal, otp, encryptedId };
});
if (!transactionResult || !transactionResult.otp) {
throw new ApiError(500, 'Failed to generate OTP');
}
// await sendOtpEmailForHost(newUser?.emailAddress, otpResult.otp);
// Send OTP email outside the DB transaction
// await sendOtpEmailForHost(transactionResult.newUser.emailAddress, transactionResult.otp);
return {
statusCode: 200,