@zapal/payload-email-sendgrid
Version:
Payload CMS SendGrid Email Adapter
137 lines (136 loc) • 6.14 kB
JavaScript
import { APIError } from 'payload';
/**
* Email adapter for [SendGrid](https://sendgrid.com) REST API
*/ export const sendGridAdapter = (args)=>{
const { apiKey, defaultFromAddress, defaultFromName } = args;
const adapter = ()=>({
name: 'sendgrid-rest',
defaultFromAddress,
defaultFromName,
sendEmail: async (message)=>{
// Map the Payload email options to SendGrid email options
const sendEmailOptions = mapPayloadEmailToSendGridEmail(message, defaultFromAddress, defaultFromName);
const res = await fetch('https://api.sendgrid.com/v3/mail/send', {
body: JSON.stringify(sendEmailOptions),
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
method: 'POST'
});
if (res.status === 202) return;
else {
const data = await res.json();
let formattedError = `Error sending email: ${res.status} ${res.statusText}${data.id ? ` (ID: ${data.id})` : ''}.`;
(data.errors || []).forEach(({ message, field, help }, idx)=>{
if (field && message) formattedError += `${idx !== 0 ? '; ' : ' '}${field && field !== 'null' ? `Field: ${field}, ` : ''}Message: ${message}${help ? `, Help: ${help} ` : ''}`;
});
throw new APIError(formattedError, res.status);
}
}
});
return adapter;
};
function mapPayloadEmailToSendGridEmail(message, defaultFromAddress, defaultFromName) {
const cc = mapAddresses(message.cc);
const bcc = mapAddresses(message.bcc);
const attachments = mapAttachments(message.attachments);
const email = {
// Required
from: mapFromAddress(message.from, defaultFromName, defaultFromAddress),
subject: message.subject ?? '',
personalizations: [
{
// Required
to: mapAddresses(message.to),
// Other To fields
cc: cc.length ? cc : undefined,
bcc: bcc.length ? cc : undefined
}
],
// Optional
attachments: attachments?.length ? attachments : undefined
};
if (message.html || message.text) {
email.content = [];
if (message.text) email.content.push({
type: 'text/plain',
value: message.text.toString?.() || ''
});
if (message.html) email.content.push({
type: 'text/html',
value: message.html.toString?.() || ''
});
}
if (message.replyTo) {
if (message.replyTo === 'string') {
email.reply_to = {
email: extractEmailFromAddressString(message.replyTo),
name: extractNameFromAddressString(message.replyTo) === message.replyTo ? undefined : extractNameFromAddressString(message.replyTo)
};
} else if (Array.isArray(message.replyTo)) {
const addresses = mapAddresses(message.replyTo);
if (addresses.length) {
if (addresses.length > 1) email.reply_to_list = addresses;
else email.reply_to = addresses[0];
}
} else {
email.reply_to = {
email: extractEmailFromAddressString(message.replyTo.address),
name: message.replyTo.name === message.replyTo.address ? undefined : message.replyTo.name
};
}
}
return email;
}
const extractEmailFromAddressString = (address)=>address.trim().replace(/.*<(.*)>/, '$1').trim();
const extractNameFromAddressString = (address)=>address.trim().replace(/(.*)<.*>/, '$1').trim().replaceAll(/^"|"$/g, '').trim().replaceAll(/^'|'$/g, '').trim();
function mapFromAddress(address, defaultFromName, defaultFromAddress) {
if (!address) return {
email: defaultFromAddress,
name: defaultFromName
};
if (typeof address === 'string') return {
email: extractEmailFromAddressString(address),
name: extractNameFromAddressString(address) === address ? undefined : extractNameFromAddressString(address)
};
return {
email: extractEmailFromAddressString(address.address),
name: address.name === address.address ? undefined : address.name
};
}
function mapAddresses(addresses) {
if (!addresses) return [];
if (typeof addresses === 'string') return [
{
email: extractEmailFromAddressString(addresses),
name: extractNameFromAddressString(addresses) === addresses ? undefined : extractNameFromAddressString(addresses)
}
];
if (Array.isArray(addresses)) return addresses.map((address)=>({
email: typeof address === 'string' ? extractEmailFromAddressString(address) : extractEmailFromAddressString(address.address),
name: typeof address === 'string' ? extractNameFromAddressString(address) === address ? undefined : extractNameFromAddressString(address) : address.name === address.address ? undefined : address.name
}));
return [
{
email: extractEmailFromAddressString(addresses.address),
name: addresses.name === addresses.address ? undefined : addresses.name
}
];
}
function mapAttachments(attachments) {
if (!attachments) return undefined;
return attachments.map((attachment)=>{
if (!attachment.filename || !attachment.content) throw new APIError('Attachment is missing filename or content', 400);
if (typeof attachment.content === 'string') return {
content: Buffer.from(attachment.content),
filename: attachment.filename
};
if (attachment.content instanceof Buffer) return {
content: attachment.content,
filename: attachment.filename
};
throw new APIError('Attachment content must be a string or a buffer', 400);
});
}
//# sourceMappingURL=index.js.map