* feat(server): album's email notification * same size button * skeleton for album invite and album update event * album invite content * album update * fix(server): smtp certificate validation (#9506) * album update content * send mail * album invite with thumbnail * pr feedback * styling * Send email to update album event * better naming * add tests * Update album-invite.email.tsx Co-authored-by: bo0tzz <git@bo0tzz.me> * Update album-update.email.tsx Co-authored-by: bo0tzz <git@bo0tzz.me> * fix: unit tests * typo * Update server/src/services/notification.service.ts Co-authored-by: Jason Rasmussen <jrasm91@gmail.com> * PR feedback * Update server/src/emails/album-update.email.tsx Co-authored-by: Zack Pollard <zackpollard@ymail.com> --------- Co-authored-by: Michel Heusschen <59014050+michelheusschen@users.noreply.github.com> Co-authored-by: bo0tzz <git@bo0tzz.me> Co-authored-by: Jason Rasmussen <jrasm91@gmail.com> Co-authored-by: Zack Pollard <zackpollard@ymail.com>
90 lines
2.7 KiB
TypeScript
90 lines
2.7 KiB
TypeScript
import { Inject, Injectable } from '@nestjs/common';
|
|
import { render } from '@react-email/render';
|
|
import { createTransport } from 'nodemailer';
|
|
import React from 'react';
|
|
import { AlbumInviteEmail } from 'src/emails/album-invite.email';
|
|
import { AlbumUpdateEmail } from 'src/emails/album-update.email';
|
|
import { WelcomeEmail } from 'src/emails/welcome.email';
|
|
import { ILoggerRepository } from 'src/interfaces/logger.interface';
|
|
import {
|
|
EmailRenderRequest,
|
|
EmailTemplate,
|
|
INotificationRepository,
|
|
SendEmailOptions,
|
|
SendEmailResponse,
|
|
SmtpOptions,
|
|
} from 'src/interfaces/notification.interface';
|
|
import { Instrumentation } from 'src/utils/instrumentation';
|
|
|
|
@Instrumentation()
|
|
@Injectable()
|
|
export class NotificationRepository implements INotificationRepository {
|
|
constructor(@Inject(ILoggerRepository) private logger: ILoggerRepository) {
|
|
this.logger.setContext(NotificationRepository.name);
|
|
}
|
|
|
|
verifySmtp(options: SmtpOptions): Promise<true> {
|
|
const transport = this.createTransport(options);
|
|
try {
|
|
return transport.verify();
|
|
} finally {
|
|
transport.close();
|
|
}
|
|
}
|
|
|
|
renderEmail(request: EmailRenderRequest): { html: string; text: string } {
|
|
const component = this.render(request);
|
|
const html = render(component, { pretty: true });
|
|
const text = render(component, { plainText: true });
|
|
return { html, text };
|
|
}
|
|
|
|
sendEmail({ to, from, subject, html, text, smtp, imageAttachments }: SendEmailOptions): Promise<SendEmailResponse> {
|
|
this.logger.debug(`Sending email to ${to} with subject: ${subject}`);
|
|
const transport = this.createTransport(smtp);
|
|
|
|
const attachments = imageAttachments?.map((attachment) => ({
|
|
filename: attachment.filename,
|
|
path: attachment.path,
|
|
cid: attachment.cid,
|
|
}));
|
|
|
|
try {
|
|
return transport.sendMail({ to, from, subject, html, text, attachments });
|
|
} finally {
|
|
transport.close();
|
|
}
|
|
}
|
|
|
|
private render({ template, data }: EmailRenderRequest): React.FunctionComponentElement<any> {
|
|
switch (template) {
|
|
case EmailTemplate.WELCOME: {
|
|
return React.createElement(WelcomeEmail, data);
|
|
}
|
|
|
|
case EmailTemplate.ALBUM_INVITE: {
|
|
return React.createElement(AlbumInviteEmail, data);
|
|
}
|
|
|
|
case EmailTemplate.ALBUM_UPDATE: {
|
|
return React.createElement(AlbumUpdateEmail, data);
|
|
}
|
|
}
|
|
}
|
|
|
|
private createTransport(options: SmtpOptions) {
|
|
return createTransport({
|
|
host: options.host,
|
|
port: options.port,
|
|
tls: { rejectUnauthorized: !options.ignoreCert },
|
|
auth:
|
|
options.username || options.password
|
|
? {
|
|
user: options.username,
|
|
pass: options.password,
|
|
}
|
|
: undefined,
|
|
});
|
|
}
|
|
}
|