Files
backend/src/email/email.service.ts

117 lines
3.7 KiB
TypeScript

import { Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import {
getVerificationEmailTemplate,
getPasswordResetTemplate,
getWelcomeEmailTemplate,
} from './templates';
export interface EmailOptions {
to: string;
subject: string;
html: string;
text?: string;
cc?: string[];
bcc?: string[];
replyTo?: string;
}
@Injectable()
export class EmailService {
private readonly logger = new Logger(EmailService.name);
private readonly endpoint: string;
constructor(private readonly configService: ConfigService) {
this.endpoint =
this.configService.get<string>('EMAIL_API_URL') ||
'https://email.routes.hiffi.com/superlabs/v1/send';
this.logger.log(`Email service initialized → ${this.endpoint}`);
}
async sendEmail(options: EmailOptions): Promise<boolean> {
const payload: Record<string, any> = {
to: [options.to],
subject: options.subject,
html: options.html,
};
if (options.text) payload.text = options.text;
if (options.cc && options.cc.length > 0) payload.cc = options.cc;
if (options.bcc && options.bcc.length > 0) payload.bcc = options.bcc;
if (options.replyTo) payload.reply_to = options.replyTo;
try {
const res = await fetch(this.endpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});
const data = await res.json().catch(() => null);
if (!res.ok || !data?.ok) {
const errMsg = data?.error || `HTTP ${res.status}`;
this.logger.error(`Failed to send email to ${options.to}: ${errMsg}`);
return false;
}
this.logger.log(`Email sent successfully to ${options.to}`);
return true;
} catch (error: any) {
this.logger.error(`Email API request failed for ${options.to}: ${error.message}`);
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, frontendUrl });
const text = `Welcome to RE-Quest! Please verify your email by clicking this link: ${verificationUrl}`;
return this.sendEmail({
to: email,
subject: 'Verify Your Email - RE-Quest',
html,
text,
});
}
// ==========================================
// PASSWORD RESET
// ==========================================
async sendPasswordResetEmail(email: string, resetUrl: string): Promise<boolean> {
const frontendUrl = this.configService.get<string>('FRONTEND_URL') || 'http://localhost:3000';
const html = getPasswordResetTemplate({ resetUrl, frontendUrl });
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-Quest',
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-Quest, ${name || 'User'}! Your real estate journey starts now.`;
return this.sendEmail({
to: email,
subject: 'Welcome to RE-Quest!',
html,
text,
});
}
}