-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: implement SMTP email service, adding env also adding email verification #153
Changes from 9 commits
6559c99
6629101
72643b9
d520ea2
28e5c80
ce85807
c8b0589
a07728a
878e01a
df0af72
995e7db
98319d6
b64b2a2
87b1371
9dda76c
8fab45a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,7 +19,12 @@ | |
import { RefreshToken } from './refresh-token/refresh-token.model'; | ||
import { randomUUID } from 'crypto'; | ||
import { compare, hash } from 'bcrypt'; | ||
import { RefreshTokenResponse } from './auth.resolver'; | ||
import { | ||
EmailConfirmationResponse, | ||
RefreshTokenResponse, | ||
} from './auth.resolver'; | ||
import { MailService } from 'src/mail/mail.service'; | ||
import { RegisterResponse } from 'src/user/user.resolver'; | ||
|
||
@Injectable() | ||
export class AuthService { | ||
|
@@ -29,6 +34,7 @@ | |
private jwtService: JwtService, | ||
private jwtCacheService: JwtCacheService, | ||
private configService: ConfigService, | ||
private mailService: MailService, | ||
@InjectRepository(Menu) | ||
private menuRepository: Repository<Menu>, | ||
@InjectRepository(Role) | ||
|
@@ -37,7 +43,101 @@ | |
private refreshTokenRepository: Repository<RefreshToken>, | ||
) {} | ||
|
||
async register(registerUserInput: RegisterUserInput): Promise<User> { | ||
async confirmEmail(token: string): Promise<EmailConfirmationResponse> { | ||
try { | ||
const payload = await this.jwtService.verifyAsync(token); | ||
|
||
// Check if payload has the required email field | ||
if (!payload || !payload.email) { | ||
return { | ||
message: 'Invalid token format', | ||
success: false, | ||
}; | ||
} | ||
|
||
// Find user and update | ||
const user = await this.userRepository.findOne({ | ||
where: { email: payload.email }, | ||
}); | ||
|
||
if (user && !user.isEmailConfirmed) { | ||
user.isEmailConfirmed = true; | ||
await this.userRepository.save(user); | ||
|
||
return { | ||
message: 'Email confirmed successfully!', | ||
success: true, | ||
}; | ||
} | ||
|
||
return { | ||
message: 'Email already confirmed or user not found.', | ||
success: false, | ||
}; | ||
} catch (error) { | ||
return { | ||
message: 'Invalid or expired token', | ||
success: false, | ||
}; | ||
} | ||
} | ||
|
||
async sendVerificationEmail(user: User): Promise<EmailConfirmationResponse> { | ||
// Generate confirmation token | ||
const verifyToken = this.jwtService.sign( | ||
{ email: user.email }, | ||
{ expiresIn: '30m' }, | ||
); | ||
|
||
// Send confirmation email | ||
await this.mailService.sendConfirmationEmail(user.email, verifyToken); | ||
|
||
// update user last time send email time | ||
user.lastEmailSendTime = new Date(); | ||
await this.userRepository.save(user); | ||
|
||
return { | ||
message: 'Verification email sent successfully!', | ||
success: true, | ||
}; | ||
} | ||
|
||
async resendVerificationEmail(email: string) { | ||
const user = await this.userRepository.findOne({ | ||
where: { email }, | ||
}); | ||
|
||
if (!user) { | ||
throw new Error('User not found'); | ||
} | ||
|
||
if (user.isEmailConfirmed) { | ||
return { message: 'Email already confirmed!' }; | ||
} | ||
|
||
// Check if a cooldown period has passed (e.g., 1 minute) | ||
const cooldownPeriod = 1 * 60 * 1000; // 1 minute in milliseconds | ||
if ( | ||
user.lastEmailSendTime && | ||
new Date().getTime() - user.lastEmailSendTime.getTime() < cooldownPeriod | ||
) { | ||
const timeLeft = Math.ceil( | ||
(cooldownPeriod - | ||
(new Date().getTime() - user.lastEmailSendTime.getTime())) / | ||
1000, | ||
); | ||
return { | ||
message: `Please wait ${timeLeft} seconds before requesting another email`, | ||
success: false, | ||
}; | ||
} | ||
|
||
return this.sendVerificationEmail(user); | ||
} | ||
Comment on lines
+111
to
+142
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Harmonize return values with the expected GraphQL type. if (user.isEmailConfirmed) {
- return { message: 'Email already confirmed!' };
+ return { message: 'Email already confirmed!', success: true };
}
if (!user) {
- throw new Error('User not found');
+ throw new NotFoundException('User not found');
}
|
||
|
||
async register( | ||
registerUserInput: RegisterUserInput, | ||
): Promise<RegisterResponse> { | ||
const { username, email, password } = registerUserInput; | ||
|
||
// Check for existing email | ||
|
@@ -54,9 +154,13 @@ | |
username, | ||
email, | ||
password: hashedPassword, | ||
isEmailConfirmed: false, | ||
}); | ||
|
||
return this.userRepository.save(newUser); | ||
await this.userRepository.save(newUser); | ||
await this.sendVerificationEmail(newUser); | ||
|
||
return { user: newUser, message: 'Confirmation email sent' }; | ||
} | ||
|
||
async login(loginUserInput: LoginUserInput): Promise<RefreshTokenResponse> { | ||
|
@@ -70,6 +174,10 @@ | |
throw new UnauthorizedException('Invalid credentials'); | ||
} | ||
|
||
if (!user.isEmailConfirmed) { | ||
throw new Error('Email not confirmed. Please check your inbox.'); | ||
} | ||
|
||
const isPasswordValid = await compare(password, user.password); | ||
|
||
if (!isPasswordValid) { | ||
|
@@ -113,6 +221,7 @@ | |
return false; | ||
} | ||
} | ||
|
||
async logout(token: string): Promise<boolean> { | ||
try { | ||
await this.jwtService.verifyAsync(token); | ||
|
@@ -125,7 +234,7 @@ | |
} | ||
|
||
return true; | ||
} catch (error) { | ||
return false; | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import { MailerModule } from '@nestjs-modules/mailer'; | ||
import { HandlebarsAdapter } from '@nestjs-modules/mailer/dist/adapters/handlebars.adapter'; | ||
import { Module } from '@nestjs/common'; | ||
import { MailService } from './mail.service'; | ||
import { join } from 'path'; | ||
import { ConfigModule, ConfigService } from '@nestjs/config'; | ||
import { TypeOrmModule } from '@nestjs/typeorm'; | ||
import { User } from 'src/user/user.model'; | ||
import { JwtModule } from '@nestjs/jwt'; | ||
|
||
@Module({ | ||
imports: [ | ||
ConfigModule, | ||
TypeOrmModule.forFeature([User]), | ||
MailerModule.forRootAsync({ | ||
// imports: [ConfigModule], // import module if not enabled globally | ||
useFactory: async (config: ConfigService) => ({ | ||
// transport: config.get("MAIL_TRANSPORT"), | ||
// or | ||
transport: { | ||
host: config.get('MAIL_HOST'), | ||
port: config.get<number>('MAIL_PORT'), | ||
secure: false, | ||
auth: { | ||
user: config.get('MAIL_USER'), | ||
pass: config.get('MAIL_PASSWORD'), | ||
}, | ||
}, | ||
defaults: { | ||
from: `"Your App" <${config.get<string>('MAIL_FROM')}>`, | ||
}, | ||
template: { | ||
dir: join(__dirname, 'templates'), | ||
adapter: new HandlebarsAdapter(), | ||
options: { | ||
strict: true, | ||
}, | ||
}, | ||
}), | ||
inject: [ConfigService], | ||
}), | ||
JwtModule, | ||
], | ||
providers: [MailService], | ||
exports: [MailService], | ||
}) | ||
export class MailModule {} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,59 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { Injectable } from '@nestjs/common'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { MailerService } from '@nestjs-modules/mailer'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { InjectRepository } from '@nestjs/typeorm'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { User } from 'src/user/user.model'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { Repository } from 'typeorm'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { ConfigService } from '@nestjs/config'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
@Injectable() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
export class MailService { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
constructor( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
@InjectRepository(User) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
private userRepository: Repository<User>, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
private readonly mailerService: MailerService, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
private configService: ConfigService, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
) {} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+10
to
+15
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Unused user repository dependency. The constructor(
- @InjectRepository(User)
- private userRepository: Repository<User>,
private readonly mailerService: MailerService,
private configService: ConfigService,
) {} 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
async sendConfirmationEmail(email: string, token: string) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const confirmUrl = `https://${this.configService.get('MAIL_DOMAIN')}/auth/confirm?token=${token}`; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
await this.mailerService.sendMail({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
to: email, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
subject: 'Confirm Your Email', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
template: './confirmation', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
context: { confirmUrl }, // Data for template | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
async sendPasswordResetEmail(user: User, token: string) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const frontendUrl = this.configService.get('FRONTEND_URL'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const url = `${frontendUrl}/reset-password?token=${token}`; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
await this.mailerService.sendMail({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
to: user.email, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
subject: 'Password Reset Request', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
template: './passwordReset', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
context: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
name: user.username, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
firstName: user.username, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
url, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// async sendConfirmationEmail(user: User, token: string) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// const frontendUrl = this.configService.get('FRONTEND_URL'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// const url = `${frontendUrl}/confirm-email?token=${token}`; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// await this.mailerService.sendMail({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// to: user.email, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// subject: 'Welcome! Confirm Your Email', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// template: './confirmation', // This will use the confirmation.hbs template | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// context: { // Data to be sent to the template | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// name: user.username, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// firstName: user.firstName || user.username, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// url, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+1
to
+59
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Add error handling for email sending. The methods don't have error handling for cases where email sending fails. Add try/catch blocks to handle potential errors from the mailer service. async sendConfirmationEmail(email: string, token: string) {
const confirmUrl = `https://${this.configService.get('MAIL_DOMAIN')}/auth/confirm?token=${token}`;
+ try {
await this.mailerService.sendMail({
to: email,
subject: 'Confirm Your Email',
template: './confirmation',
context: { confirmUrl }, // Data for template
});
+ return true;
+ } catch (error) {
+ console.error(`Failed to send confirmation email to ${email}:`, error);
+ throw new Error('Failed to send confirmation email. Please try again later.');
+ }
} 📝 Committable suggestion
Suggested change
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
<!DOCTYPE html> | ||
<html> | ||
<head> | ||
<meta charset="UTF-8"> | ||
<title>Confirm Your Email</title> | ||
</head> | ||
<body style="font-family: Arial, sans-serif; background-color: #f4f4f4; padding: 20px; text-align: center;"> | ||
<div style="max-width: 600px; background-color: #ffffff; padding: 30px; border-radius: 8px; margin: auto;"> | ||
<h2 style="color: #333;">Welcome to CodeFox! 🎉</h2> | ||
<p style="font-size: 16px; color: #555;"> | ||
Hi there! Thanks for signing up. Please confirm your email address to activate your account. | ||
</p> | ||
<p> | ||
<a href="{{confirmUrl}}" style="display: inline-block; padding: 12px 24px; font-size: 16px; color: #fff; background-color: #007bff; text-decoration: none; border-radius: 5px;"> | ||
Confirm Email | ||
</a> | ||
</p> | ||
<p style="font-size: 14px; color: #888;"> | ||
If you didn't create an account, you can ignore this email. The confirmation link will expire in 24 hours. | ||
</p> | ||
<p style="font-size: 14px; color: #888;"> | ||
Need help? Contact our support team at <a href="mailto:support@codefox.net">support@codefox.net</a>. | ||
</p> | ||
</div> | ||
</body> | ||
</html> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
❓ Verification inconclusive
Replace placeholder email credentials with actual values
The current mail configuration contains example values that must be replaced with actual credentials before deployment.
Issues to address:
smtp.example.com
,user@example.com
, etc. with actual mail server detailsMAIL_PASSWORD
is properly secured and not committed to version controlMAIL_DOMAIN
to match your actual domain (appears to be "codefox" based on the email template)This is critical for the email service to function correctly.
Action Required: Update Email Credentials Securely
The configuration in
backend/.env
(lines 8–14) still uses placeholder values, which must be replaced with the actual mail server settings before deployment. Please address the following:smtp.example.com
,user@example.com
,noreply@example.com
, and587
with your production mail server details.your_net
to reflect your actual domain name (e.g., based on the expected value like "codefox" from the email template).This update is critical to ensure the email service functions correctly and securely in the deployed environment.