feat(auth): add OTP verification for email changes on profile update

Email changes via PATCH /api/v1/auth/profile now require OTP verification
instead of updating immediately. A 6-digit code is sent to the new email
address and must be confirmed via POST /api/v1/auth/profile/verify-email
within 10 minutes. Also fixes pre-existing web valuation test failures
(formatPrice output format, removed comparables section, missing
QueryClientProvider wrapper).

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Ho Ngoc Hai
2026-04-16 04:23:06 +07:00
parent baaeb56849
commit 43f9e23b28
19 changed files with 429 additions and 76 deletions

View File

@@ -1,7 +1,9 @@
import { Module } from '@nestjs/common';
import { CqrsModule } from '@nestjs/cqrs';
import { AuthModule } from '@modules/auth';
import { SendNotificationHandler } from './application/commands/send-notification/send-notification.handler';
import { AgentVerifiedListener } from './application/listeners/agent-verified.listener';
import { EmailChangeRequestedListener } from './application/listeners/email-change-requested.listener';
import { InquiryReceivedListener } from './application/listeners/inquiry-received.listener';
import { ListingApprovedListener } from './application/listeners/listing-approved.listener';
import { ListingRejectedListener } from './application/listeners/listing-rejected.listener';
@@ -21,8 +23,11 @@ import { PrismaNotificationPreferenceRepository } from './infrastructure/reposit
import { PrismaNotificationRepository } from './infrastructure/repositories/prisma-notification.repository';
import { EmailService } from './infrastructure/services/email.service';
import { FcmService } from './infrastructure/services/fcm.service';
import { StringeeSmsService } from './infrastructure/services/stringee-sms.service';
import { TemplateService } from './infrastructure/services/template.service';
import { ZaloOaService } from './infrastructure/services/zalo-oa.service';
import { NotificationsController } from './presentation/controllers/notifications.controller';
import { NotificationsGateway } from './presentation/gateways/notifications.gateway';
const CommandHandlers = [SendNotificationHandler];
@@ -41,10 +46,11 @@ const EventListeners = [
InquiryReceivedListener,
ListingSoldListener,
UserKycUpdatedListener,
EmailChangeRequestedListener,
];
@Module({
imports: [CqrsModule],
imports: [CqrsModule, AuthModule],
controllers: [NotificationsController],
providers: [
// Repositories
@@ -54,14 +60,19 @@ const EventListeners = [
// Services
EmailService,
FcmService,
StringeeSmsService,
ZaloOaService,
TemplateService,
// WebSocket Gateway
NotificationsGateway,
// CQRS
...CommandHandlers,
// Event Listeners
...EventListeners,
],
exports: [EmailService, FcmService, TemplateService],
exports: [EmailService, FcmService, StringeeSmsService, ZaloOaService, TemplateService, NotificationsGateway],
})
export class NotificationsModule {}