feat: add email/phone change notifications

This commit is contained in:
Sergey Krylov 2026-02-27 06:34:21 +03:00
parent 7e9e47f755
commit a4b9f46afd
4 changed files with 99 additions and 11 deletions

View File

@ -19,4 +19,13 @@ export class MailService {
html
})
}
public async sendEmailChange(email: string, code: string) {
const html = this.templateService.render('email-change', { code })
await this.transporter.sendMail({
to: email,
subject: 'Подтверждение смены почты',
html
})
}
}

View File

@ -0,0 +1,14 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Подтверждение смены почты</title>
</head>
<body>
<h1>Подтверждение смены почты</h1>
<p>Ваш код подтверждения для смены почты: {{code}}</p>
</body>
</html>

View File

@ -1,6 +1,10 @@
import { Controller } from '@nestjs/common'
import { Controller, Logger } from '@nestjs/common'
import { Ctx, EventPattern, Payload, RmqContext } from '@nestjs/microservices'
import type { OtpRequestedEvent } from '@teacinema/contracts'
import type {
EmailChangedEvent,
OtpRequestedEvent,
PhoneChangedEvent
} from '@teacinema/contracts'
import { RmqService } from '../../infra/rmq/rmq.service'
@ -8,6 +12,8 @@ import { NotificationsService } from './notifications.service'
@Controller()
export class NotificationsController {
private readonly logger = new Logger(NotificationsController.name)
constructor(
private readonly notificationsService: NotificationsService,
private readonly rmqService: RmqService
@ -22,7 +28,35 @@ export class NotificationsController {
await this.notificationsService.sendOtp(data)
this.rmqService.ack(ctx)
} catch (e) {
console.log('OTP processing error', e.message ?? e)
this.logger.error(`OTP processing error ${e.message ?? e}}`)
this.rmqService.nack(ctx)
}
}
@EventPattern('account.phone.changed')
public async phoneChangedRequested(
@Payload() data: PhoneChangedEvent,
@Ctx() ctx: RmqContext
) {
try {
await this.notificationsService.phoneChanged(data)
this.rmqService.ack(ctx)
} catch (e) {
this.logger.error(`Phone changing processing error ${e.message ?? e}}`)
this.rmqService.nack(ctx)
}
}
@EventPattern('account.email.changed')
public async emaileChangedRequested(
@Payload() data: EmailChangedEvent,
@Ctx() ctx: RmqContext
) {
try {
await this.notificationsService.emailChanged(data)
this.rmqService.ack(ctx)
} catch (e) {
this.logger.error(`Email changing processing error ${e.message ?? e}}`)
this.rmqService.nack(ctx)
}
}

View File

@ -1,20 +1,51 @@
import { Injectable } from '@nestjs/common'
import { OtpRequestedEvent } from '@teacinema/contracts'
import { Injectable, Logger } from '@nestjs/common'
import {
EmailChangedEvent,
OtpRequestedEvent,
PhoneChangedEvent
} from '@teacinema/contracts'
import { MailService } from '../../infra/mail/mail.service'
import { SmsService } from '../../infra/sms/sms.service'
@Injectable()
export class NotificationsService {
constructor(private readonly mailService: MailService) {}
private readonly logger = new Logger(NotificationsService.name)
constructor(
private readonly mailService: MailService,
private readonly smsService: SmsService
) {}
public async sendOtp(data: OtpRequestedEvent) {
const { identifier, type, code } = data
if (type === 'email') {
console.log('SEND EMAIL', data)
this.logger.log(`Send OTP to ${identifier} via ${type}`)
switch (type) {
case 'email':
await this.mailService.sendOtp(identifier, code)
} else {
console.log('SMS')
break
case 'phone':
await this.smsService.sendOtp(identifier, code)
break
default:
this.logger.error(`Unknown notification type: ${type}`)
throw new Error(`Unknown type: ${type}`)
}
}
public async phoneChanged(data: PhoneChangedEvent) {
const { phone, code } = data
this.logger.log(`Change phone to ${phone} via phone`)
return await this.smsService.sendPhoneChanged(phone, code)
}
public async emailChanged(data: EmailChangedEvent) {
const { email, code } = data
this.logger.log(`Change email to ${email} via email`)
return await this.mailService.sendEmailChange(email, code)
}
}