Files
goodgo-platform/apps/api/src/modules/notifications/infrastructure/services/email.service.ts
Ho Ngoc Hai 0b29fac35e feat(notifications): add multi-channel notification module with Email, FCM, templates, and event listeners
- Domain: NotificationLog/NotificationPreference entities, repositories, channel value object
- Infrastructure: EmailService (nodemailer/SMTP), FcmService (firebase-admin), TemplateService (Handlebars)
- Application: SendNotification CQRS command, UserRegistered + AgentVerified event listeners
- Presentation: NotificationsController with history, preferences, and templates endpoints
- Prisma: NotificationLog and NotificationPreference models with proper indexes
- Templates: Vietnamese notification templates for user.registered, agent.verified, listing.approved, inquiry.received, password.reset

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-08 01:42:17 +07:00

64 lines
1.7 KiB
TypeScript

import { Injectable, type OnModuleInit } from '@nestjs/common';
import { LoggerService } from '@modules/shared/infrastructure/logger.service';
import * as nodemailer from 'nodemailer';
export interface SendEmailDto {
to: string;
subject: string;
html: string;
}
@Injectable()
export class EmailService implements OnModuleInit {
private transporter!: nodemailer.Transporter;
constructor(private readonly logger: LoggerService) {}
onModuleInit(): void {
const host = process.env['SMTP_HOST'] ?? 'localhost';
const port = Number(process.env['SMTP_PORT'] ?? '1025');
const user = process.env['SMTP_USER'];
const pass = process.env['SMTP_PASS'];
this.transporter = nodemailer.createTransport({
host,
port,
secure: port === 465,
...(user && pass ? { auth: { user, pass } } : {}),
});
this.logger.log(`Email transport configured: ${host}:${port}`, 'EmailService');
}
async send(dto: SendEmailDto): Promise<{ messageId: string }> {
const from = process.env['SMTP_FROM'] ?? 'noreply@goodgo.vn';
try {
const info = await this.transporter.sendMail({
from,
to: dto.to,
subject: dto.subject,
html: dto.html,
});
this.logger.log(`Email sent to ${dto.to}: ${info.messageId}`, 'EmailService');
return { messageId: info.messageId };
} catch (error) {
this.logger.error(
`Failed to send email to ${dto.to}: ${error instanceof Error ? error.message : String(error)}`,
'EmailService',
);
throw error;
}
}
async verify(): Promise<boolean> {
try {
await this.transporter.verify();
return true;
} catch {
return false;
}
}
}