2025-01-23 08:31:30 -05:00
|
|
|
import { Injectable } from '@nestjs/common';
|
2024-05-02 16:43:18 +02:00
|
|
|
import { render } from '@react-email/render';
|
|
|
|
|
import { createTransport } from 'nodemailer';
|
|
|
|
|
import React from 'react';
|
2024-05-28 09:16:46 +07:00
|
|
|
import { AlbumInviteEmail } from 'src/emails/album-invite.email';
|
|
|
|
|
import { AlbumUpdateEmail } from 'src/emails/album-update.email';
|
2024-06-07 11:34:09 -05:00
|
|
|
import { TestEmail } from 'src/emails/test.email';
|
2024-05-02 16:43:18 +02:00
|
|
|
import { WelcomeEmail } from 'src/emails/welcome.email';
|
2025-01-23 08:31:30 -05:00
|
|
|
import { LoggingRepository } from 'src/repositories/logging.repository';
|
2025-02-11 17:15:56 -05:00
|
|
|
import { EmailImageAttachment } from 'src/types';
|
2025-01-23 18:10:17 -05:00
|
|
|
|
|
|
|
|
export type SendEmailOptions = {
|
|
|
|
|
from: string;
|
|
|
|
|
to: string;
|
|
|
|
|
replyTo?: string;
|
|
|
|
|
subject: string;
|
|
|
|
|
html: string;
|
|
|
|
|
text: string;
|
|
|
|
|
imageAttachments?: EmailImageAttachment[];
|
|
|
|
|
smtp: SmtpOptions;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export type SmtpOptions = {
|
|
|
|
|
host: string;
|
|
|
|
|
port?: number;
|
|
|
|
|
username?: string;
|
|
|
|
|
password?: string;
|
|
|
|
|
ignoreCert?: boolean;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export enum EmailTemplate {
|
|
|
|
|
TEST_EMAIL = 'test',
|
|
|
|
|
|
|
|
|
|
// AUTH
|
|
|
|
|
WELCOME = 'welcome',
|
|
|
|
|
RESET_PASSWORD = 'reset-password',
|
|
|
|
|
|
|
|
|
|
// ALBUM
|
|
|
|
|
ALBUM_INVITE = 'album-invite',
|
|
|
|
|
ALBUM_UPDATE = 'album-update',
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface BaseEmailProps {
|
|
|
|
|
baseUrl: string;
|
|
|
|
|
customTemplate?: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface TestEmailProps extends BaseEmailProps {
|
|
|
|
|
displayName: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface WelcomeEmailProps extends BaseEmailProps {
|
|
|
|
|
displayName: string;
|
|
|
|
|
username: string;
|
|
|
|
|
password?: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface AlbumInviteEmailProps extends BaseEmailProps {
|
|
|
|
|
albumName: string;
|
|
|
|
|
albumId: string;
|
|
|
|
|
senderName: string;
|
|
|
|
|
recipientName: string;
|
|
|
|
|
cid?: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface AlbumUpdateEmailProps extends BaseEmailProps {
|
|
|
|
|
albumName: string;
|
|
|
|
|
albumId: string;
|
|
|
|
|
recipientName: string;
|
|
|
|
|
cid?: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export type EmailRenderRequest =
|
|
|
|
|
| {
|
|
|
|
|
template: EmailTemplate.TEST_EMAIL;
|
|
|
|
|
data: TestEmailProps;
|
|
|
|
|
customTemplate: string;
|
|
|
|
|
}
|
|
|
|
|
| {
|
|
|
|
|
template: EmailTemplate.WELCOME;
|
|
|
|
|
data: WelcomeEmailProps;
|
|
|
|
|
customTemplate: string;
|
|
|
|
|
}
|
|
|
|
|
| {
|
|
|
|
|
template: EmailTemplate.ALBUM_INVITE;
|
|
|
|
|
data: AlbumInviteEmailProps;
|
|
|
|
|
customTemplate: string;
|
|
|
|
|
}
|
|
|
|
|
| {
|
|
|
|
|
template: EmailTemplate.ALBUM_UPDATE;
|
|
|
|
|
data: AlbumUpdateEmailProps;
|
|
|
|
|
customTemplate: string;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export type SendEmailResponse = {
|
|
|
|
|
messageId: string;
|
|
|
|
|
response: any;
|
|
|
|
|
};
|
|
|
|
|
|
2024-05-02 16:43:18 +02:00
|
|
|
@Injectable()
|
2025-04-21 12:53:37 -04:00
|
|
|
export class EmailRepository {
|
2025-01-23 08:31:30 -05:00
|
|
|
constructor(private logger: LoggingRepository) {
|
2025-04-21 12:53:37 -04:00
|
|
|
this.logger.setContext(EmailRepository.name);
|
2024-05-02 16:43:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
verifySmtp(options: SmtpOptions): Promise<true> {
|
|
|
|
|
const transport = this.createTransport(options);
|
|
|
|
|
try {
|
|
|
|
|
return transport.verify();
|
|
|
|
|
} finally {
|
|
|
|
|
transport.close();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-28 12:00:10 -04:00
|
|
|
async renderEmail(request: EmailRenderRequest): Promise<{ html: string; text: string }> {
|
2024-05-02 16:43:18 +02:00
|
|
|
const component = this.render(request);
|
2025-01-08 08:53:08 -06:00
|
|
|
const html = await render(component, { pretty: false });
|
2024-08-28 12:00:10 -04:00
|
|
|
const text = await render(component, { plainText: true });
|
2024-05-02 16:43:18 +02:00
|
|
|
return { html, text };
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-28 09:16:46 +07:00
|
|
|
sendEmail({ to, from, subject, html, text, smtp, imageAttachments }: SendEmailOptions): Promise<SendEmailResponse> {
|
2024-05-02 16:43:18 +02:00
|
|
|
this.logger.debug(`Sending email to ${to} with subject: ${subject}`);
|
|
|
|
|
const transport = this.createTransport(smtp);
|
2024-05-28 09:16:46 +07:00
|
|
|
|
|
|
|
|
const attachments = imageAttachments?.map((attachment) => ({
|
|
|
|
|
filename: attachment.filename,
|
|
|
|
|
path: attachment.path,
|
|
|
|
|
cid: attachment.cid,
|
|
|
|
|
}));
|
|
|
|
|
|
2024-05-02 16:43:18 +02:00
|
|
|
try {
|
2024-05-28 09:16:46 +07:00
|
|
|
return transport.sendMail({ to, from, subject, html, text, attachments });
|
2024-05-02 16:43:18 +02:00
|
|
|
} finally {
|
|
|
|
|
transport.close();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-04 21:26:02 +01:00
|
|
|
private render({ template, data, customTemplate }: EmailRenderRequest): React.FunctionComponentElement<any> {
|
2024-05-02 16:43:18 +02:00
|
|
|
switch (template) {
|
2024-06-07 11:34:09 -05:00
|
|
|
case EmailTemplate.TEST_EMAIL: {
|
2024-12-04 21:26:02 +01:00
|
|
|
return React.createElement(TestEmail, { ...data, customTemplate });
|
2024-06-07 11:34:09 -05:00
|
|
|
}
|
|
|
|
|
|
2024-05-02 16:43:18 +02:00
|
|
|
case EmailTemplate.WELCOME: {
|
2024-12-04 21:26:02 +01:00
|
|
|
return React.createElement(WelcomeEmail, { ...data, customTemplate });
|
2024-05-02 16:43:18 +02:00
|
|
|
}
|
2024-05-28 09:16:46 +07:00
|
|
|
|
|
|
|
|
case EmailTemplate.ALBUM_INVITE: {
|
2024-12-04 21:26:02 +01:00
|
|
|
return React.createElement(AlbumInviteEmail, { ...data, customTemplate });
|
2024-05-28 09:16:46 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case EmailTemplate.ALBUM_UPDATE: {
|
2024-12-04 21:26:02 +01:00
|
|
|
return React.createElement(AlbumUpdateEmail, { ...data, customTemplate });
|
2024-05-28 09:16:46 +07:00
|
|
|
}
|
2024-05-02 16:43:18 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private createTransport(options: SmtpOptions) {
|
|
|
|
|
return createTransport({
|
|
|
|
|
host: options.host,
|
|
|
|
|
port: options.port,
|
2024-05-15 13:21:35 +02:00
|
|
|
tls: { rejectUnauthorized: !options.ignoreCert },
|
2024-05-02 16:43:18 +02:00
|
|
|
auth:
|
|
|
|
|
options.username || options.password
|
|
|
|
|
? {
|
|
|
|
|
user: options.username,
|
|
|
|
|
pass: options.password,
|
|
|
|
|
}
|
|
|
|
|
: undefined,
|
2024-06-07 11:34:09 -05:00
|
|
|
connectionTimeout: 5000,
|
2024-05-02 16:43:18 +02:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|