From f92cee0575d8574d559212f0837ec1ab412ffd5d Mon Sep 17 00:00:00 2001 From: SepehrYahyaee <7heycallmegray@gmail.com> Date: Wed, 1 Jul 2026 12:11:19 +0330 Subject: [PATCH] YARA-1075 --- .../request-management.service.ts | 85 ++++++++++++------- 1 file changed, 54 insertions(+), 31 deletions(-) diff --git a/src/request-management/request-management.service.ts b/src/request-management/request-management.service.ts index a4b8168..49572d0 100644 --- a/src/request-management/request-management.service.ts +++ b/src/request-management/request-management.service.ts @@ -2092,14 +2092,32 @@ export class RequestManagementService { (req.parties[secondIdx].person as any).phoneNumber = phoneNumber; } + // If a valid OTP is already active for this number, return 200 immediately — + // no duplicate SMS, no unnecessary write, no error shown to the expert. + const canonicalPhoneAdv = normalizeIranMobile(phoneNumber) ?? phoneNumber; + const existingUserAdv = await this.userDbService.findOne( + buildUserLookupByPhone(canonicalPhoneAdv), + ); + if (existingUserAdv && isOtpExpiryActive(existingUserAdv.otpExpire)) { + return { + requestId: req._id, + publicId: req.publicId, + workflow: req.workflow, + message: `OTP already sent to ${phoneNumber} and is still valid. Collect the code and call verify-party-otp.`, + }; + } + // Send OTP to second party — expert collects it from them in person try { await this.userAuthService.sendOtpRequest(phoneNumber); } catch (e: any) { if (e?.message?.includes("Wait for expiry")) { - throw new BadRequestException( - `(${phoneNumber}): OTP was recently sent. Wait for the expiry time before requesting again.`, - ); + return { + requestId: req._id, + publicId: req.publicId, + workflow: req.workflow, + message: `OTP already sent to ${phoneNumber} and is still valid. Collect the code and call verify-party-otp.`, + }; } throw e; } @@ -4548,29 +4566,32 @@ export class RequestManagementService { ); } const sent: string[] = []; - try { - await this.userAuthService.sendOtpRequest(dto.firstPartyPhoneNumber); - sent.push(dto.firstPartyPhoneNumber); - } catch (e: any) { - if (e?.message?.includes("Wait for expiry")) { - throw new BadRequestException( - `First party (${dto.firstPartyPhoneNumber}): OTP was recently sent. Wait for the expiry time before requesting again.`, - ); + + // Helper: send OTP or silently succeed when a valid OTP already exists. + const sendOrSkipIfActive = async (phone: string): Promise => { + const canonical = normalizeIranMobile(phone) ?? phone; + const existing = await this.userDbService.findOne( + buildUserLookupByPhone(canonical), + ); + if (existing && isOtpExpiryActive(existing.otpExpire)) { + sent.push(phone); // already has a live OTP — treat as sent + return; } - throw e; - } - if (dto.secondPartyPhoneNumber) { try { - await this.userAuthService.sendOtpRequest(dto.secondPartyPhoneNumber); - sent.push(dto.secondPartyPhoneNumber); + await this.userAuthService.sendOtpRequest(phone); + sent.push(phone); } catch (e: any) { if (e?.message?.includes("Wait for expiry")) { - throw new BadRequestException( - `Second party (${dto.secondPartyPhoneNumber}): OTP was recently sent. Wait for the expiry time before requesting again.`, - ); + sent.push(phone); // race: sendOtpRequest found it active too — treat as sent + return; } throw e; } + }; + + await sendOrSkipIfActive(dto.firstPartyPhoneNumber); + if (dto.secondPartyPhoneNumber) { + await sendOrSkipIfActive(dto.secondPartyPhoneNumber); } return { sent: true, @@ -4749,32 +4770,34 @@ export class RequestManagementService { new Date(event.timestamp) > twoMinutesAgo, ); if (recentlySentToPhone) { - throw new BadRequestException( - `(${phone}): OTP was sent recently. Please wait 2 minutes before requesting again.`, - ); + return { + sent: true, + message: `OTP already sent to ${phone} and is still valid. Collect the code from the party, then call verify-party-otp.`, + }; } // --- DB-level cooldown guard (catches burst/concurrent requests before history is saved) --- // Read the user's otpExpire directly from the users collection. If a live - // OTP already exists we return 400 immediately — no SMS call, no DB writes. - const canonicalPhone = - normalizeIranMobile(phone) ?? phone; + // OTP already exists return 200 immediately — no duplicate SMS, no DB writes. + const canonicalPhone = normalizeIranMobile(phone) ?? phone; const existingUser = await this.userDbService.findOne( buildUserLookupByPhone(canonicalPhone), ); if (existingUser && isOtpExpiryActive(existingUser.otpExpire)) { - throw new BadRequestException( - `(${phone}): OTP was sent recently. Please wait before requesting again.`, - ); + return { + sent: true, + message: `OTP already sent to ${phone} and is still valid. Collect the code from the party, then call verify-party-otp.`, + }; } try { await this.userAuthService.sendOtpRequest(phone); } catch (e: any) { if (e?.message?.includes("Wait for expiry")) { - throw new BadRequestException( - `(${phone}): OTP was recently sent. Wait for the expiry time before requesting again.`, - ); + return { + sent: true, + message: `OTP already sent to ${phone} and is still valid. Collect the code from the party, then call verify-party-otp.`, + }; } throw e; }