Skip to content

Commit

Permalink
feat: migrate from mimetext to mail-composer for raw emails
Browse files Browse the repository at this point in the history
  • Loading branch information
BlankParticle committed Aug 29, 2024
1 parent 422e5d2 commit 740cfc0
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 82 deletions.
1 change: 0 additions & 1 deletion apps/mail-bridge/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
"mailauth": "^4.6.9",
"mailparser": "^3.7.1",
"mime": "^4.0.4",
"mimetext": "^3.0.24",
"mysql2": "^3.11.0",
"nanoid": "^5.0.7",
"nodemailer": "^6.9.14",
Expand Down
4 changes: 1 addition & 3 deletions apps/mail-bridge/smtp/sendEmail.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@ import type { AuthOptions } from './auth';
export type SendEmailOptions = {
to: string[];
from: string;
headers: Record<string, string>;
raw: string;
};

export async function sendEmail({
auth: { host, port, username, password, encryption, authMethod },
email: { to, from, headers, raw }
email: { to, from, raw }
}: {
auth: AuthOptions;
email: SendEmailOptions;
Expand All @@ -27,7 +26,6 @@ export async function sendEmail({
});
const res = await transport.sendMail({
envelope: { to, from },
headers,
raw
});
return res;
Expand Down
78 changes: 32 additions & 46 deletions apps/mail-bridge/trpc/routers/sendMailRouter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ import {
walkAndReplaceImages
} from '../../utils/tiptap-utils';
import { createExtensionSet } from '@u22n/tiptap/extensions';
import MailComposer from 'nodemailer/lib/mail-composer';
import { and, eq, inArray } from '@u22n/database/orm';
import { type JSONContent } from '@u22n/tiptap/react';
import { typeIdValidator } from '@u22n/utils/typeid';
import { router, protectedProcedure } from '../trpc';
import { sendEmail } from '../../smtp/sendEmail';
import { createMimeMessage } from 'mimetext';
import { tiptapHtml } from '@u22n/tiptap';
import { z } from 'zod';

Expand Down Expand Up @@ -666,13 +666,6 @@ export const sendMailRouter = router({
const replyToEmailId =
convoEntryResponse.replyTo?.convoMessageSource.emailMessageId ?? null;

const emailHeaders: Record<string, string> = replyToEmailId
? {
'In-Reply-To': `<${replyToEmailId}>`,
References: `<${replyToEmailId}>`
}
: {};

// remove duplicates in the CC metadata if they exist in the TO metadata or from metadata
const convoMetadataCcAddressesFiltered = convoMetadataCcAddresses.filter(
(ccAddress) => {
Expand All @@ -683,41 +676,33 @@ export const sendMailRouter = router({
}
);

const rawEmail = createMimeMessage();
rawEmail.setTo(convoToAddress);
if (convoCcAddressesFiltered.length > 0) {
rawEmail.setCc(convoCcAddressesFiltered);
}
rawEmail.setSender({
name: sendAsEmailIdentity.sendName ?? undefined,
addr: convoSenderEmailAddress
});
rawEmail.setSubject(convoEntryResponse.subject?.subject ?? 'No Subject');
rawEmail.addMessage({
contentType: 'text/plain',
data: emailBodyPlainText
});
rawEmail.addMessage({
contentType: 'text/html',
data: emailBodyHTML
});
for (const attachment of postalAttachments) {
rawEmail.addAttachment({
contentType: attachment.content_type,
data: attachment.data,
const mail = new MailComposer({
to: convoToAddress,
cc:
convoCcAddressesFiltered.length > 0
? convoCcAddressesFiltered
: undefined,
from: sendAsEmailIdentity.sendName
? {
name: sendAsEmailIdentity.sendName,
address: convoSenderEmailAddress
}
: convoSenderEmailAddress,
subject: convoEntryResponse.subject?.subject ?? 'No Subject',
text: emailBodyPlainText,
html: emailBodyHTML,
attachments: postalAttachments.map((attachment) => ({
content: attachment.data,
filename: attachment.name,
inline: attachment.inline,
headers: {
'Content-ID': attachment.content_id,
'Content-Type': attachment.content_type
}
});
}
rawEmail.setHeaders(emailHeaders);
rawEmail.setHeader(
'Message-ID',
`<${convoEntryResponse.publicId.substring(3)}_${convoResponse.publicId.substring(2)}@${sendAsEmailIdentity.domainName}>`
);
contentType: attachment.content_type,
cid: attachment.inline ? attachment.content_id : undefined
})),
inReplyTo: replyToEmailId ?? undefined,
references: replyToEmailId ? [replyToEmailId] : undefined,
messageId: `${convoEntryResponse.publicId.substring(3)}_${convoResponse.publicId.substring(2)}@${sendAsEmailIdentity.domainName}`
});

const rawEmail = await mail.compile().build();

// If there is external email credentials then the identity is external, use their smtp server instead of postal's
if (sendAsEmailIdentity.externalCredentials) {
Expand All @@ -727,9 +712,10 @@ export const sendMailRouter = router({
auth,
email: {
to: [`${convoToAddress}`, ...convoCcAddressesFiltered],
from: `${sendAsEmailIdentity.sendName} <${convoSenderEmailAddress}>`,
headers: emailHeaders,
raw: rawEmail.asRaw()
from: sendAsEmailIdentity.sendName
? `${sendAsEmailIdentity.sendName} <${convoSenderEmailAddress}>`
: convoSenderEmailAddress,
raw: rawEmail.toString('utf-8')
}
})
.then((res) => ({ success: true, ...res }))
Expand Down Expand Up @@ -817,7 +803,7 @@ export const sendMailRouter = router({
body: JSON.stringify({
mail_from: convoSenderEmailAddress,
rcpt_to: [`${convoToAddress}`, ...convoCcAddressesFiltered],
data: Buffer.from(rawEmail.asRaw()).toString('base64')
data: rawEmail.toString('base64')
})
})
.then((res) => res.json())
Expand Down
32 changes: 0 additions & 32 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 740cfc0

Please sign in to comment.