diff --git a/serverless.yml b/serverless.yml index d7eb8d3..9311ae4 100644 --- a/serverless.yml +++ b/serverless.yml @@ -199,6 +199,21 @@ functions: path: /stepper method: get + getSuggestion: + handler: src/modules/minglaradmin/handlers/getSuggestion.handler + package: + patterns: + - 'src/modules/minglaradmin/**' + - 'common/**' + - 'src/common/**' + - 'node_modules/@prisma/client/**' + - 'node_modules/.prisma/**' + + events: + - httpApi: + path: /minglaradmin/get-suggestion + method: get + minglarRegistration: handler: src/modules/minglaradmin/handlers/registration.handler package: @@ -337,21 +352,6 @@ functions: path: /minglaradmin/add-suggestion method: post - getSuggestion: - handler: src/modules/minglaradmin/handlers/getSuggestion.handler - package: - patterns: - - 'src/modules/minglaradmin/**' - - 'common/**' - - 'src/common/**' - - 'node_modules/@prisma/client/**' - - 'node_modules/.prisma/**' - - events: - - httpApi: - path: /minglaradmin/get-suggestion - method: get - getAllCoadminAndAMDetails: handler: src/modules/minglaradmin/handlers/getAllCoadminAndAM.handler package: diff --git a/src/common/utils/constants/minglar.constant.ts b/src/common/utils/constants/minglar.constant.ts index c33278d..f3d598a 100644 --- a/src/common/utils/constants/minglar.constant.ts +++ b/src/common/utils/constants/minglar.constant.ts @@ -24,8 +24,7 @@ export const MINGLAR_INVITATION_STATUS = { } export const HOST_SUGGESTION_TITLES = { - SETUP_PROFILE: "Setup Profile", - REVIEW_ACCOUNT: "Review Account", - ADD_PAYMENT_DETAILS: "Add Payment Details", - AGREEMENT: "Agreement" + COMPANY_DETAILS: "Complete Details", + COMPANY_DOCUMENTATION:"Company documentataion", + COMPANY_SOCIAL_PROOF:"Social Proof", } \ No newline at end of file diff --git a/src/modules/minglaradmin/handlers/getSuggestion.ts b/src/modules/host/handlers/getSuggestion.ts similarity index 87% rename from src/modules/minglaradmin/handlers/getSuggestion.ts rename to src/modules/host/handlers/getSuggestion.ts index ce6c5d7..5dcfb4d 100644 --- a/src/modules/minglaradmin/handlers/getSuggestion.ts +++ b/src/modules/host/handlers/getSuggestion.ts @@ -1,9 +1,9 @@ import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda'; import { safeHandler } from '../../../common/utils/handlers/safeHandler'; import { PrismaService } from '../../../common/database/prisma.service'; -import { MinglarService } from '../services/minglar.service'; +import { MinglarService } from '../../minglaradmin/services/minglar.service'; import ApiError from '../../../common/utils/helper/ApiError'; -import { verifyMinglarAdminToken } from '../../../common/middlewares/jwt/authForMinglarAdmin'; +import { verifyHostToken } from '@/common/middlewares/jwt/authForHost'; const prismaService = new PrismaService(); const minglarService = new MinglarService(prismaService); @@ -23,7 +23,7 @@ export const handler = safeHandler(async ( } // Verify token and get user info - const userInfo = await verifyMinglarAdminToken(token); + const userInfo = await verifyHostToken(token); // Get user details including role const user = await prismaService.user.findUnique({ diff --git a/src/modules/host/handlers/registration.ts b/src/modules/host/handlers/registration.ts index 4f42cdf..bb5cb0d 100644 --- a/src/modules/host/handlers/registration.ts +++ b/src/modules/host/handlers/registration.ts @@ -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,38 +31,61 @@ export const handler = safeHandler(async ( throw new ApiError(400, 'Email is required'); } - const user = await prismaService.user.findUnique({ - where: { emailAddress: email }, - select: { emailAddress: true, id: true, userPassword: true }, + // 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 }, + }); + + if (user && user.userPassword) { + throw new ApiError(409, 'User is already registered. Please login.'); + } + + let newUserLocal; + + if (user && !user.userPassword) { + // reuse existing invited user record + newUserLocal = user; + } else { + // create new user record within the transaction + newUserLocal = await tx.user.create({ + data: { emailAddress: email, roleXid: ROLE.HOST }, + }); + } + + // 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 + + // 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 (user && user.userPassword) { - throw new ApiError(409, 'User is already registered. Please login.'); + if (!transactionResult || !transactionResult.otp) { + throw new ApiError(500, 'Failed to generate OTP'); } - let newUser; - - if (user && !user.userPassword) { - // ✅ User already exists but without password → reuse record - newUser = user; - } else { - // ✅ No user found → create new one - newUser = await hostService.createMinglarUser(email); - } - - const otpResult = await generateOtpHelper( - Number(newUser?.id), - newUser?.emailAddress, - 'Register', - 6, - 5 - ); - - if (!otpResult || !otpResult.otp) { - throw new ApiError(500, 'Failed to send OTP'); - } - - // await sendOtpEmailForHost(newUser?.emailAddress, otpResult.otp); + // Send OTP email outside the DB transaction + // await sendOtpEmailForHost(transactionResult.newUser.emailAddress, transactionResult.otp); return { statusCode: 200, diff --git a/src/modules/minglaradmin/handlers/inviteTeammate.ts b/src/modules/minglaradmin/handlers/inviteTeammate.ts index 3b925c3..8f1cee2 100644 --- a/src/modules/minglaradmin/handlers/inviteTeammate.ts +++ b/src/modules/minglaradmin/handlers/inviteTeammate.ts @@ -78,22 +78,31 @@ export const handler = safeHandler(async ( throw new ApiError(400, 'Per value must be greater than 0'); } - // Check if user already exists - using service - const existingUser = await minglarService.checkUserExists(emailAddress); + // Run user creation, revenue and invite details inside a transaction + const createdUser = await prismaService.$transaction(async (tx) => { + // create a transaction-scoped MinglarService instance + const txMinglarService = new MinglarService(tx as unknown as PrismaService); - if (existingUser) { - throw new ApiError(400, 'User already exists.'); - } + // Check if user already exists within transaction to avoid race + const existingUser = await txMinglarService.checkUserExists(emailAddress); + if (existingUser) { + throw new ApiError(400, 'User already exists.'); + } - // Create new user - using service - const user = await minglarService.createUserForInvite(emailAddress, roleXid); + // Create new user + const user = await txMinglarService.createUserForInvite(emailAddress, roleXid); - // Create user revenue - using service - await minglarService.createUserRevenue(user.id, isFixedSalary, perValue || 0); + // Create user revenue + await txMinglarService.createUserRevenue(user.id, isFixedSalary, perValue || 0); - // Create invite details - using service - await minglarService.createInviteDetails(user.id, userInfo.id, MINGLAR_INVITATION_STATUS.INVITED); + // Create invite details + await txMinglarService.createInviteDetails(user.id, userInfo.id, MINGLAR_INVITATION_STATUS.INVITED); + // return created user from transaction + return user; + }); + + // send email after transaction commits await sendInvitationEmailForMinglarAdmin(emailAddress); return {