From 5a9d0d6155d337ef1e720bf8cde85ae7ba20c51a Mon Sep 17 00:00:00 2001 From: paritosh18 Date: Wed, 19 Nov 2025 17:20:05 +0530 Subject: [PATCH 1/4] GetSuggstion --- prisma/schema.prisma | 4 ++-- src/modules/minglaradmin/handlers/getSuggestion.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 9939ffa..34e1cde 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -743,8 +743,8 @@ model HostSuggestion { comments String @map("comments") isparent Boolean @default(false) @map("is_parent") isreviewed Boolean @default(false) @map("is_reviewed") - reviewedByXid Int @map("reviewed_by_xid") - reviewedBy User @relation("UserReviewedSuggestions", fields: [reviewedByXid], references: [id], onDelete: Cascade) + reviewedByXid Int? @map("reviewed_by_xid") + reviewedBy User? @relation("UserReviewedSuggestions", fields: [reviewedByXid], references: [id], onDelete: Cascade) reviewOn DateTime? @map("review_on") isActive Boolean @default(true) @map("is_active") createdAt DateTime @default(now()) @map("created_at") diff --git a/src/modules/minglaradmin/handlers/getSuggestion.ts b/src/modules/minglaradmin/handlers/getSuggestion.ts index ce6c5d7..4202c92 100644 --- a/src/modules/minglaradmin/handlers/getSuggestion.ts +++ b/src/modules/minglaradmin/handlers/getSuggestion.ts @@ -3,7 +3,7 @@ import { safeHandler } from '../../../common/utils/handlers/safeHandler'; import { PrismaService } from '../../../common/database/prisma.service'; import { MinglarService } from '../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({ From 3a29e70b2b709e05fd0ad6f87c69f86099ed26d7 Mon Sep 17 00:00:00 2001 From: paritosh18 Date: Wed, 19 Nov 2025 18:47:00 +0530 Subject: [PATCH 2/4] CHange path of of GetSuggestion --- serverless.yml | 30 +++++++++---------- .../utils/constants/minglar.constant.ts | 7 ++--- .../handlers/getSuggestion.ts | 2 +- 3 files changed, 19 insertions(+), 20 deletions(-) rename src/modules/{minglaradmin => host}/handlers/getSuggestion.ts (95%) diff --git a/serverless.yml b/serverless.yml index 6083dab..7d34ad3 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 95% rename from src/modules/minglaradmin/handlers/getSuggestion.ts rename to src/modules/host/handlers/getSuggestion.ts index 4202c92..5dcfb4d 100644 --- a/src/modules/minglaradmin/handlers/getSuggestion.ts +++ b/src/modules/host/handlers/getSuggestion.ts @@ -1,7 +1,7 @@ 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 { verifyHostToken } from '@/common/middlewares/jwt/authForHost'; From f61287a11d1f0c57234e83e2014a670f6b9391be Mon Sep 17 00:00:00 2001 From: paritosh18 Date: Thu, 20 Nov 2025 16:40:50 +0530 Subject: [PATCH 3/4] update transaction in invite teammates --- .../minglaradmin/handlers/inviteTeammate.ts | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) 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 { From 00dd080ee0f2129128c661831a01a33847ccd01d Mon Sep 17 00:00:00 2001 From: paritosh18 Date: Thu, 20 Nov 2025 16:57:10 +0530 Subject: [PATCH 4/4] Refactor registration handler to use a transaction for user creation and OTP generation; implement OTP storage and user role assignment --- src/modules/host/handlers/registration.ts | 84 +++++++++++++++-------- 1 file changed, 55 insertions(+), 29 deletions(-) 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,