Improving Email DRYness (#2486)
* Email DRY * WIP * Improve email DRYness Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
This commit is contained in:
41
apps/web/lib/emails/templates/_base-email.ts
Normal file
41
apps/web/lib/emails/templates/_base-email.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import nodemailer from "nodemailer";
|
||||
|
||||
import { getErrorFromUnknown } from "@calcom/lib/errors";
|
||||
import { serverConfig } from "@calcom/lib/serverConfig";
|
||||
|
||||
export default class BaseEmail {
|
||||
name: string = "";
|
||||
|
||||
protected getNodeMailerPayload(): Record<string, unknown> {
|
||||
return {};
|
||||
}
|
||||
public sendEmail() {
|
||||
new Promise((resolve, reject) =>
|
||||
nodemailer
|
||||
.createTransport(this.getMailerOptions().transport)
|
||||
.sendMail(this.getNodeMailerPayload(), (_err, info) => {
|
||||
if (_err) {
|
||||
const err = getErrorFromUnknown(_err);
|
||||
this.printNodeMailerError(err);
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(info);
|
||||
}
|
||||
})
|
||||
).catch((e) => console.error("sendEmail", e));
|
||||
return new Promise((resolve) => resolve("send mail async"));
|
||||
}
|
||||
|
||||
protected getMailerOptions() {
|
||||
return {
|
||||
transport: serverConfig.transport,
|
||||
from: serverConfig.from,
|
||||
};
|
||||
}
|
||||
|
||||
protected printNodeMailerError(error: Error): void {
|
||||
/** Don't clog the logs with unsent emails in E2E */
|
||||
if (process.env.NEXT_PUBLIC_IS_E2E) return;
|
||||
console.error(`${this.name}_ERROR`, error);
|
||||
}
|
||||
}
|
||||
@@ -4,22 +4,20 @@ import timezone from "dayjs/plugin/timezone";
|
||||
import toArray from "dayjs/plugin/toArray";
|
||||
import utc from "dayjs/plugin/utc";
|
||||
import { createEvent, DateArray } from "ics";
|
||||
import { DatasetJsonLdProps } from "next-seo";
|
||||
import nodemailer from "nodemailer";
|
||||
import rrule from "rrule";
|
||||
|
||||
import { getAppName } from "@calcom/app-store/utils";
|
||||
import { getCancelLink, getRichDescription } from "@calcom/lib/CalEventParser";
|
||||
import { getErrorFromUnknown } from "@calcom/lib/errors";
|
||||
import { serverConfig } from "@calcom/lib/serverConfig";
|
||||
import type { Person, CalendarEvent, RecurringEvent } from "@calcom/types/Calendar";
|
||||
import type { CalendarEvent, Person, RecurringEvent } from "@calcom/types/Calendar";
|
||||
|
||||
import BaseEmail from "@lib/emails/templates/_base-email";
|
||||
|
||||
import {
|
||||
emailHead,
|
||||
emailSchedulingBodyHeader,
|
||||
emailBodyLogo,
|
||||
emailHead,
|
||||
emailScheduledBodyHeaderContent,
|
||||
emailSchedulingBodyDivider,
|
||||
emailSchedulingBodyHeader,
|
||||
linkIcon,
|
||||
} from "./common";
|
||||
|
||||
@@ -28,34 +26,19 @@ dayjs.extend(timezone);
|
||||
dayjs.extend(localizedFormat);
|
||||
dayjs.extend(toArray);
|
||||
|
||||
export default class AttendeeScheduledEmail {
|
||||
export default class AttendeeScheduledEmail extends BaseEmail {
|
||||
calEvent: CalendarEvent;
|
||||
attendee: Person;
|
||||
recurringEvent: RecurringEvent;
|
||||
|
||||
constructor(calEvent: CalendarEvent, attendee: Person, recurringEvent: RecurringEvent) {
|
||||
super();
|
||||
this.name = "SEND_BOOKING_CONFIRMATION";
|
||||
this.calEvent = calEvent;
|
||||
this.attendee = attendee;
|
||||
this.recurringEvent = recurringEvent;
|
||||
}
|
||||
|
||||
public sendEmail() {
|
||||
new Promise((resolve, reject) =>
|
||||
nodemailer
|
||||
.createTransport(this.getMailerOptions().transport)
|
||||
.sendMail(this.getNodeMailerPayload(), (_err, info) => {
|
||||
if (_err) {
|
||||
const err = getErrorFromUnknown(_err);
|
||||
this.printNodeMailerError(err);
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(info);
|
||||
}
|
||||
})
|
||||
).catch((e) => console.error("sendEmail", e));
|
||||
return new Promise((resolve) => resolve("send mail async"));
|
||||
}
|
||||
|
||||
protected getiCalEventAsString(): string | undefined {
|
||||
// Taking care of recurrence rule beforehand
|
||||
let recurrenceRule: string | undefined = undefined;
|
||||
@@ -115,12 +98,6 @@ export default class AttendeeScheduledEmail {
|
||||
};
|
||||
}
|
||||
|
||||
protected getMailerOptions() {
|
||||
return {
|
||||
transport: serverConfig.transport,
|
||||
from: serverConfig.from,
|
||||
};
|
||||
}
|
||||
protected getDescription(): string {
|
||||
if (!this.calEvent.description) return "";
|
||||
return `
|
||||
@@ -144,10 +121,6 @@ ${getRichDescription(this.calEvent)}
|
||||
`.trim();
|
||||
}
|
||||
|
||||
protected printNodeMailerError(error: Error): void {
|
||||
console.error("SEND_BOOKING_CONFIRMATION_ERROR", this.attendee.email, error);
|
||||
}
|
||||
|
||||
protected getHtmlBody(): string {
|
||||
const headerContent = this.calEvent.attendees[0].language.translate("confirmed_event_type_subject", {
|
||||
eventType: this.calEvent.type,
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import { TFunction } from "next-i18next";
|
||||
import nodemailer from "nodemailer";
|
||||
|
||||
import { getErrorFromUnknown } from "@calcom/lib/errors";
|
||||
import { serverConfig } from "@calcom/lib/serverConfig";
|
||||
import BaseEmail from "@lib/emails/templates/_base-email";
|
||||
|
||||
import { emailHead, linkIcon, emailBodyLogo } from "./common";
|
||||
import { emailBodyLogo, emailHead, linkIcon } from "./common";
|
||||
|
||||
export type PasswordReset = {
|
||||
language: TFunction;
|
||||
@@ -17,37 +15,15 @@ export type PasswordReset = {
|
||||
|
||||
export const PASSWORD_RESET_EXPIRY_HOURS = 6;
|
||||
|
||||
export default class ForgotPasswordEmail {
|
||||
export default class ForgotPasswordEmail extends BaseEmail {
|
||||
passwordEvent: PasswordReset;
|
||||
|
||||
constructor(passwordEvent: PasswordReset) {
|
||||
super();
|
||||
this.name = "SEND_PASSWORD_RESET_EMAIL";
|
||||
this.passwordEvent = passwordEvent;
|
||||
}
|
||||
|
||||
public sendEmail() {
|
||||
new Promise((resolve, reject) =>
|
||||
nodemailer
|
||||
.createTransport(this.getMailerOptions().transport)
|
||||
.sendMail(this.getNodeMailerPayload(), (_err, info) => {
|
||||
if (_err) {
|
||||
const err = getErrorFromUnknown(_err);
|
||||
this.printNodeMailerError(err);
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(info);
|
||||
}
|
||||
})
|
||||
).catch((e) => console.error("sendEmail", e));
|
||||
return new Promise((resolve) => resolve("send mail async"));
|
||||
}
|
||||
|
||||
protected getMailerOptions() {
|
||||
return {
|
||||
transport: serverConfig.transport,
|
||||
from: serverConfig.from,
|
||||
};
|
||||
}
|
||||
|
||||
protected getNodeMailerPayload(): Record<string, unknown> {
|
||||
return {
|
||||
to: `${this.passwordEvent.user.name} <${this.passwordEvent.user.email}>`,
|
||||
@@ -58,10 +34,6 @@ export default class ForgotPasswordEmail {
|
||||
};
|
||||
}
|
||||
|
||||
protected printNodeMailerError(error: Error): void {
|
||||
console.error("SEND_PASSWORD_RESET_EMAIL_ERROR", this.passwordEvent.user.email, error);
|
||||
}
|
||||
|
||||
protected getTextBody(): string {
|
||||
return `
|
||||
${this.passwordEvent.language("reset_password_subject")}
|
||||
|
||||
@@ -4,21 +4,20 @@ import timezone from "dayjs/plugin/timezone";
|
||||
import toArray from "dayjs/plugin/toArray";
|
||||
import utc from "dayjs/plugin/utc";
|
||||
import { createEvent, DateArray, Person } from "ics";
|
||||
import nodemailer from "nodemailer";
|
||||
import rrule from "rrule";
|
||||
|
||||
import { getAppName } from "@calcom/app-store/utils";
|
||||
import { getCancelLink, getRichDescription } from "@calcom/lib/CalEventParser";
|
||||
import { getErrorFromUnknown } from "@calcom/lib/errors";
|
||||
import { serverConfig } from "@calcom/lib/serverConfig";
|
||||
import type { CalendarEvent, RecurringEvent } from "@calcom/types/Calendar";
|
||||
|
||||
import BaseEmail from "@lib/emails/templates/_base-email";
|
||||
|
||||
import {
|
||||
emailHead,
|
||||
emailSchedulingBodyHeader,
|
||||
emailBodyLogo,
|
||||
emailHead,
|
||||
emailScheduledBodyHeaderContent,
|
||||
emailSchedulingBodyDivider,
|
||||
emailSchedulingBodyHeader,
|
||||
linkIcon,
|
||||
} from "./common";
|
||||
|
||||
@@ -27,32 +26,17 @@ dayjs.extend(timezone);
|
||||
dayjs.extend(localizedFormat);
|
||||
dayjs.extend(toArray);
|
||||
|
||||
export default class OrganizerScheduledEmail {
|
||||
export default class OrganizerScheduledEmail extends BaseEmail {
|
||||
calEvent: CalendarEvent;
|
||||
recurringEvent: RecurringEvent;
|
||||
|
||||
constructor(calEvent: CalendarEvent, recurringEvent: RecurringEvent) {
|
||||
super();
|
||||
this.name = "SEND_BOOKING_CONFIRMATION";
|
||||
this.calEvent = calEvent;
|
||||
this.recurringEvent = recurringEvent;
|
||||
}
|
||||
|
||||
public sendEmail() {
|
||||
new Promise((resolve, reject) =>
|
||||
nodemailer
|
||||
.createTransport(this.getMailerOptions().transport)
|
||||
.sendMail(this.getNodeMailerPayload(), (_err, info) => {
|
||||
if (_err) {
|
||||
const err = getErrorFromUnknown(_err);
|
||||
this.printNodeMailerError(err);
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(info);
|
||||
}
|
||||
})
|
||||
).catch((e) => console.error("sendEmail", e));
|
||||
return new Promise((resolve) => resolve("send mail async"));
|
||||
}
|
||||
|
||||
protected getiCalEventAsString(): string | undefined {
|
||||
// Taking care of recurrence rule beforehand
|
||||
let recurrenceRule: string | undefined = undefined;
|
||||
@@ -121,13 +105,6 @@ export default class OrganizerScheduledEmail {
|
||||
};
|
||||
}
|
||||
|
||||
protected getMailerOptions() {
|
||||
return {
|
||||
transport: serverConfig.transport,
|
||||
from: serverConfig.from,
|
||||
};
|
||||
}
|
||||
|
||||
protected getTextBody(): string {
|
||||
return `
|
||||
${this.calEvent.organizer.language.translate(
|
||||
@@ -139,10 +116,6 @@ ${getRichDescription(this.calEvent)}
|
||||
`.trim();
|
||||
}
|
||||
|
||||
protected printNodeMailerError(error: Error): void {
|
||||
console.error("SEND_BOOKING_CONFIRMATION_ERROR", this.calEvent.organizer.email, error);
|
||||
}
|
||||
|
||||
protected getHtmlBody(): string {
|
||||
const headerContent = this.calEvent.organizer.language.translate("confirmed_event_type_subject", {
|
||||
eventType: this.calEvent.type,
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import { TFunction } from "next-i18next";
|
||||
import nodemailer from "nodemailer";
|
||||
|
||||
import { getErrorFromUnknown } from "@calcom/lib/errors";
|
||||
import { serverConfig } from "@calcom/lib/serverConfig";
|
||||
import BaseEmail from "@lib/emails/templates/_base-email";
|
||||
|
||||
import { emailHead, linkIcon, emailBodyLogo } from "./common";
|
||||
import { emailBodyLogo, emailHead, linkIcon } from "./common";
|
||||
|
||||
export type TeamInvite = {
|
||||
language: TFunction;
|
||||
@@ -14,37 +12,15 @@ export type TeamInvite = {
|
||||
joinLink: string;
|
||||
};
|
||||
|
||||
export default class TeamInviteEmail {
|
||||
export default class TeamInviteEmail extends BaseEmail {
|
||||
teamInviteEvent: TeamInvite;
|
||||
|
||||
constructor(teamInviteEvent: TeamInvite) {
|
||||
super();
|
||||
this.name = "SEND_TEAM_INVITE_EMAIL";
|
||||
this.teamInviteEvent = teamInviteEvent;
|
||||
}
|
||||
|
||||
public sendEmail() {
|
||||
new Promise((resolve, reject) =>
|
||||
nodemailer
|
||||
.createTransport(this.getMailerOptions().transport)
|
||||
.sendMail(this.getNodeMailerPayload(), (_err, info) => {
|
||||
if (_err) {
|
||||
const err = getErrorFromUnknown(_err);
|
||||
this.printNodeMailerError(err);
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(info);
|
||||
}
|
||||
})
|
||||
).catch((e) => console.error("sendEmail", e));
|
||||
return new Promise((resolve) => resolve("send mail async"));
|
||||
}
|
||||
|
||||
protected getMailerOptions() {
|
||||
return {
|
||||
transport: serverConfig.transport,
|
||||
from: serverConfig.from,
|
||||
};
|
||||
}
|
||||
|
||||
protected getNodeMailerPayload(): Record<string, unknown> {
|
||||
return {
|
||||
to: this.teamInviteEvent.to,
|
||||
@@ -58,10 +34,6 @@ export default class TeamInviteEmail {
|
||||
};
|
||||
}
|
||||
|
||||
protected printNodeMailerError(error: Error): void {
|
||||
console.error("SEND_TEAM_INVITE_EMAIL_ERROR", this.teamInviteEvent.to, error);
|
||||
}
|
||||
|
||||
protected getTextBody(): string {
|
||||
return "";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user