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

136
src/email/email.service.ts Normal file
View File

@@ -0,0 +1,136 @@
import { Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import * as nodemailer from 'nodemailer';
import { Transporter } from 'nodemailer';
import {
getVerificationEmailTemplate,
getPasswordResetTemplate,
getWelcomeEmailTemplate,
} from './templates';
export interface EmailOptions {
to: string;
subject: string;
html: string;
text?: string;
}
@Injectable()
export class EmailService {
private readonly logger = new Logger(EmailService.name);
private transporter: Transporter;
constructor(private readonly configService: ConfigService) {
this.createTransporter();
}
private createTransporter() {
const host = this.configService.get<string>('SMTP_HOST');
const port = this.configService.get<number>('SMTP_PORT');
const user = this.configService.get<string>('SMTP_USER');
const pass = this.configService.get<string>('SMTP_PASS');
if (!host || !user || !pass) {
this.logger.warn('SMTP configuration incomplete. Emails will be logged only.');
return;
}
this.transporter = nodemailer.createTransport({
host,
port: port || 465,
secure: true, // SSL/TLS
auth: {
user,
pass,
},
});
// Verify connection
this.transporter.verify((error) => {
if (error) {
this.logger.error('SMTP connection failed:', error);
} else {
this.logger.log('SMTP connection established successfully');
}
});
}
async sendEmail(options: EmailOptions): Promise<boolean> {
const from = this.configService.get<string>('SMTP_FROM') ||
this.configService.get<string>('SMTP_USER');
if (!this.transporter) {
this.logger.warn('No SMTP transporter. Logging email instead:');
this.logger.log(`To: ${options.to}`);
this.logger.log(`Subject: ${options.subject}`);
this.logger.log(`Body: ${options.text || 'HTML email'}`);
return false;
}
try {
const info = await this.transporter.sendMail({
from: `"RE-QuestN" <${from}>`,
to: options.to,
subject: options.subject,
html: options.html,
text: options.text,
});
this.logger.log(`Email sent successfully: ${info.messageId}`);
return true;
} catch (error) {
this.logger.error(`Failed to send email to ${options.to}:`, error);
return false;
}
}
// ==========================================
// EMAIL VERIFICATION
// ==========================================
async sendVerificationEmail(email: string, token: string, name?: string): Promise<boolean> {
const frontendUrl = this.configService.get<string>('FRONTEND_URL') || 'http://localhost:3000';
const verificationUrl = `${frontendUrl}/verify-email?token=${token}`;
const html = getVerificationEmailTemplate({ verificationUrl, name });
const text = `Welcome to RE-QuestN! Please verify your email by clicking this link: ${verificationUrl}`;
return this.sendEmail({
to: email,
subject: 'Verify Your Email - RE-QuestN',
html,
text,
});
}
// ==========================================
// PASSWORD RESET
// ==========================================
async sendPasswordResetEmail(email: string, resetUrl: string): Promise<boolean> {
const html = getPasswordResetTemplate({ resetUrl });
const text = `Reset your password by clicking this link: ${resetUrl}. This link will expire in 1 hour.`;
return this.sendEmail({
to: email,
subject: 'Reset Your Password - RE-QuestN',
html,
text,
});
}
// ==========================================
// WELCOME EMAIL
// ==========================================
async sendWelcomeEmail(email: string, name?: string): Promise<boolean> {
const frontendUrl = this.configService.get<string>('FRONTEND_URL') || 'http://localhost:3000';
const html = getWelcomeEmailTemplate({ name, frontendUrl });
const text = `Welcome to RE-QuestN, ${name || 'User'}! Your real estate journey starts now.`;
return this.sendEmail({
to: email,
subject: 'Welcome to RE-QuestN!',
html,
text,
});
}
}