SMTP settings
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { render } from '@react-email/render';
|
||||
import transport from './transport';
|
||||
import { getTransport } from './transport';
|
||||
import { templates } from './templates';
|
||||
|
||||
type SendMailArgs = {
|
||||
@@ -10,22 +10,15 @@ type SendMailArgs = {
|
||||
from?: string;
|
||||
};
|
||||
|
||||
export async function sendMail({ template, subject, to, data, from = 'Officer <no-reply@officer.dev>' }: SendMailArgs) {
|
||||
const options = {
|
||||
from,
|
||||
to,
|
||||
subject,
|
||||
};
|
||||
export async function sendMail({ template, subject, to, data, from }: SendMailArgs) {
|
||||
const { transport, from: configuredFrom } = await getTransport();
|
||||
const sender = from ?? configuredFrom ?? 'officerdev <no-reply@officer.dev>';
|
||||
|
||||
const theTemplate = templates[template];
|
||||
const templated = theTemplate(data);
|
||||
|
||||
// try {
|
||||
const emailHtml = await render(templated);
|
||||
transport.sendMail({ ...options, html: emailHtml });
|
||||
// } catch (ex) {
|
||||
// console.log("ex", ex);
|
||||
// }
|
||||
await transport.sendMail({ from: sender, to, subject, html: emailHtml });
|
||||
}
|
||||
|
||||
export { templates };
|
||||
export { getTransport, templates };
|
||||
|
||||
@@ -1,6 +1,70 @@
|
||||
import { createTransport } from 'nodemailer';
|
||||
import { createTransport, type Transporter } from 'nodemailer';
|
||||
import { homedir } from 'node:os';
|
||||
|
||||
const { MAIL_TRANSPORT } = process.env;
|
||||
|
||||
const transport = createTransport(MAIL_TRANSPORT);
|
||||
type SmtpConfig = {
|
||||
provider: 'resend' | 'smtp' | 'mailhog';
|
||||
apiKey?: string;
|
||||
host?: string;
|
||||
port?: number;
|
||||
username?: string;
|
||||
password?: string;
|
||||
secure?: boolean;
|
||||
fromName: string;
|
||||
fromEmail: string;
|
||||
};
|
||||
|
||||
export default transport;
|
||||
const settingsPath = `${homedir()}/.config/officer.dev/server-settings.json`;
|
||||
|
||||
let cachedTransport: Transporter | null = null;
|
||||
let cachedConfigHash: string | null = null;
|
||||
|
||||
function buildTransportUrl(config: SmtpConfig): string {
|
||||
if (config.provider === 'resend') {
|
||||
return `smtps://resend:${config.apiKey}@smtp.resend.com:465`;
|
||||
}
|
||||
if (config.provider === 'mailhog') {
|
||||
return `smtp://${config.host ?? 'localhost'}:${config.port ?? 1025}`;
|
||||
}
|
||||
const auth = config.username ? `${encodeURIComponent(config.username)}:${encodeURIComponent(config.password ?? '')}@` : '';
|
||||
const protocol = config.secure ? 'smtps' : 'smtp';
|
||||
return `${protocol}://${auth}${config.host}:${config.port ?? 587}`;
|
||||
}
|
||||
|
||||
type TransportResult = {
|
||||
transport: Transporter;
|
||||
from: string | null;
|
||||
};
|
||||
|
||||
export async function getTransport(): Promise<TransportResult> {
|
||||
try {
|
||||
const file = Bun.file(settingsPath);
|
||||
if (await file.exists()) {
|
||||
const settings = await file.json();
|
||||
const smtp: SmtpConfig | undefined = settings.smtp;
|
||||
if (smtp) {
|
||||
const hash = JSON.stringify(smtp);
|
||||
if (cachedTransport && cachedConfigHash === hash) {
|
||||
return { transport: cachedTransport, from: `${smtp.fromName} <${smtp.fromEmail}>` };
|
||||
}
|
||||
const url = buildTransportUrl(smtp);
|
||||
cachedTransport = createTransport(url);
|
||||
cachedConfigHash = hash;
|
||||
return { transport: cachedTransport, from: `${smtp.fromName} <${smtp.fromEmail}>` };
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Fall through to env var
|
||||
}
|
||||
|
||||
if (MAIL_TRANSPORT) {
|
||||
if (!cachedTransport || cachedConfigHash !== 'env') {
|
||||
cachedTransport = createTransport(MAIL_TRANSPORT);
|
||||
cachedConfigHash = 'env';
|
||||
}
|
||||
return { transport: cachedTransport, from: null };
|
||||
}
|
||||
|
||||
throw new Error('No email transport configured');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user