feat: Implement email service with welcome, password reset, and verification templates, and add application logo.

This commit is contained in:
pradeepkumar
2025-12-22 11:57:16 +05:30
parent 158a617600
commit 1ab86b488a
17 changed files with 742 additions and 11 deletions

View File

@@ -19,6 +19,7 @@ import {
ForgotPasswordDto,
ResetPasswordDto,
RefreshTokenDto,
VerifyEmailDto,
} from './dto';
import { UserRole, AuthProvider, UserStatus } from '@prisma/client';
@@ -88,11 +89,13 @@ export class AuthService {
// Create session
await this.createSession(user.id, tokens.accessToken, tokens.refreshToken);
// Emit event for email verification (can be handled by email service)
// Emit event for email verification (handled by email service)
this.eventEmitter.emit('user.registered', {
userId: user.id,
email: user.email,
role: user.role,
firstName: dto.firstName,
lastName: dto.lastName,
});
return {
@@ -404,6 +407,110 @@ export class AuthService {
}
}
// ==========================================
// VERIFY EMAIL
// ==========================================
async verifyEmail(dto: VerifyEmailDto) {
try {
const payload = this.jwtService.verify(dto.token, {
secret: this.configService.get<string>('jwt.secret'),
});
if (payload.type !== 'email-verification') {
throw new BadRequestException('Invalid verification token');
}
const user = await this.prisma.user.findUnique({
where: { id: payload.sub },
include: {
userProfile: true,
agentProfile: true,
},
});
if (!user) {
throw new NotFoundException('User not found');
}
if (user.emailVerified) {
return {
message: 'Email is already verified',
};
}
// Update user as verified
await this.prisma.user.update({
where: { id: user.id },
data: {
emailVerified: true,
emailVerifiedAt: new Date(),
},
});
// Get profile for name
const profile =
user.role === UserRole.AGENT ? user.agentProfile : user.userProfile;
// Emit event for welcome email
this.eventEmitter.emit('user.email-verified', {
email: user.email,
name: profile?.firstName,
});
return {
message: 'Email verified successfully',
};
} catch (error) {
if (error.name === 'TokenExpiredError') {
throw new BadRequestException('Verification token has expired');
}
if (error.name === 'JsonWebTokenError') {
throw new BadRequestException('Invalid verification token');
}
throw error;
}
}
// ==========================================
// RESEND VERIFICATION EMAIL
// ==========================================
async resendVerificationEmail(userId: string) {
const user = await this.prisma.user.findUnique({
where: { id: userId },
include: {
userProfile: true,
agentProfile: true,
},
});
if (!user) {
throw new NotFoundException('User not found');
}
if (user.emailVerified) {
return {
message: 'Email is already verified',
};
}
// Get profile for name
const profile =
user.role === UserRole.AGENT ? user.agentProfile : user.userProfile;
// Emit event for email service to send verification
this.eventEmitter.emit('user.registered', {
userId: user.id,
email: user.email,
role: user.role,
firstName: profile?.firstName,
lastName: profile?.lastName,
});
return {
message: 'Verification email sent',
};
}
// ==========================================
// REFRESH TOKEN
// ==========================================
@@ -597,8 +704,16 @@ export class AuthService {
),
);
await this.prisma.session.create({
data: {
// Use upsert to handle potential duplicate tokens
await this.prisma.session.upsert({
where: { token: accessToken },
update: {
refreshToken,
userAgent,
ipAddress,
expiresAt,
},
create: {
userId,
token: accessToken,
refreshToken,