Files
traxxx-web/src/email.js

85 lines
2.1 KiB
JavaScript

import config from 'config';
import { Resend } from 'resend';
import { emailTemplates } from './email-templates.js';
import initLogger from './logger.js';
const logger = initLogger();
const resend = (() => {
if (config.email.enabled) {
try {
return new Resend(config.email.apiKey);
}
catch (error) {
logger.error(`Failed to initialize Resend: ${error.message}`);
}
}
return null;
})();
export async function renderEmail(template, { to, subject, props }) {
const { html, text } = await emailTemplates.render(`${template}.vue`, {
props: {
...props,
address: config.web.address,
},
});
return {
from: config.email.from,
to,
subject,
html,
text,
};
}
// always batches, even for a single item: Resend's batch endpoint accepts a batch of one,
// and passing batchValidation: 'permissive' means one bad recipient among many doesn't
// sink the rest of the batch (the default 'strict' mode fails the whole call instead)
export async function sendEmails(items) {
if (!config.email.enabled) {
return null;
}
if (!resend) {
logger.warn('E-mails are enabled, but Resend is not initialized');
return null;
}
const rendered = await Promise.all(items.map(async (item) => {
try {
return await renderEmail(item.template, item);
}
catch (error) {
logger.error(`Failed to render '${item.template}' e-mail for '${item.to}': ${error.message}${JSON.stringify(item.props)}`);
return null;
}
}));
const sentItems = items.filter((item, index) => rendered[index]);
const emails = rendered.filter(Boolean);
if (emails.length === 0) {
return null;
}
const { data, error } = await resend.batch.send(emails, { batchValidation: 'permissive' });
if (error) {
logger.error(`Failed to send ${emails.length} e-mail(s) (${error.statusCode} ${error.name}): ${error.message}`);
return null;
}
data.errors?.forEach(({ index, message }) => {
const item = sentItems[index];
logger.error(`Failed to send '${item.template}' e-mail to '${item.to}': ${message}${JSON.stringify(item.props)}`);
});
logger.info(`Sent ${emails.length - (data.errors?.length || 0)}/${items.length} e-mail(s)`);
return data;
}