diff --git a/src/servers/api/server-settings/smtp.ts b/src/servers/api/server-settings/smtp.ts
index c5425787..fc7bcc91 100644
--- a/src/servers/api/server-settings/smtp.ts
+++ b/src/servers/api/server-settings/smtp.ts
@@ -35,9 +35,6 @@ function serializeConfig(smtp: SmtpConfig) {
}
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}`;
}
@@ -72,8 +69,20 @@ smtpRouter.put('/', async (ctx) => {
smtpRouter.post('/test-connection', async (ctx) => {
try {
- const { transport } = await getTransport();
- await transport.verify();
+ const result = await getTransport();
+
+ if (result.type === 'resend') {
+ const res = await fetch('https://api.resend.com/domains', {
+ headers: { 'Authorization': `Bearer ${result.apiKey}` },
+ });
+ if (!res.ok) {
+ const err = await res.json();
+ throw new Error(err.message ?? `Resend API error: ${res.status}`);
+ }
+ return ctx.json({ success: true });
+ }
+
+ await result.transport.verify();
return ctx.json({ success: true });
} catch (err) {
const message = err instanceof Error ? err.message : 'Unknown error';
@@ -93,17 +102,29 @@ smtpRouter.post('/test', async (ctx) => {
if (body.password?.includes('****')) body.password = saved.password;
}
+ const from = `${body.fromName} <${body.fromEmail}>`;
+ const testHtml = '
Officer Test Email
If you received this, your email configuration is working correctly.
';
+
try {
+ if (body.provider === 'resend') {
+ const res = await fetch('https://api.resend.com/emails', {
+ method: 'POST',
+ headers: {
+ 'Authorization': `Bearer ${body.apiKey}`,
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({ from, to: body.to, subject: 'Officer Test Email', html: testHtml }),
+ });
+ if (!res.ok) {
+ const err = await res.json();
+ throw new Error(err.message ?? `Resend API error: ${res.status}`);
+ }
+ return ctx.json({ success: true });
+ }
+
const url = buildTransportUrl(body);
const transport = createTransport(url);
- const from = `${body.fromName} <${body.fromEmail}>`;
-
- await transport.sendMail({
- from,
- to: body.to,
- subject: 'Officer Test Email',
- html: 'Officer Test Email
If you received this, your email configuration is working correctly.
',
- });
+ await transport.sendMail({ from, to: body.to, subject: 'Officer Test Email', html: testHtml });
return ctx.json({ success: true });
} catch (err) {
const message = err instanceof Error ? err.message : 'Unknown error';
diff --git a/src/workspaces/emailer/src/index.ts b/src/workspaces/emailer/src/index.ts
index 5c60c681..fc58293b 100644
--- a/src/workspaces/emailer/src/index.ts
+++ b/src/workspaces/emailer/src/index.ts
@@ -11,14 +11,32 @@ type SendMailArgs = {
};
export async function sendMail({ template, subject, to, data, from }: SendMailArgs) {
- const { transport, from: configuredFrom } = await getTransport();
- const sender = from ?? configuredFrom ?? 'officerdev ';
+ const result = await getTransport();
+ const sender = from ?? result.from ?? 'officerdev ';
const theTemplate = templates[template];
const templated = theTemplate(data);
const emailHtml = await render(templated);
- await transport.sendMail({ from: sender, to, subject, html: emailHtml });
+
+ if (result.type === 'resend') {
+ const res = await fetch('https://api.resend.com/emails', {
+ method: 'POST',
+ headers: {
+ 'Authorization': `Bearer ${result.apiKey}`,
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({ from: sender, to, subject, html: emailHtml }),
+ });
+ if (!res.ok) {
+ const err = await res.json();
+ throw new Error(err.message ?? `Resend API error: ${res.status}`);
+ }
+ return;
+ }
+
+ await result.transport.sendMail({ from: sender, to, subject, html: emailHtml });
}
export { getTransport, templates };
+export type { TransportResult } from './transport';
diff --git a/src/workspaces/emailer/src/transport.ts b/src/workspaces/emailer/src/transport.ts
index 8220108b..94a1af3c 100644
--- a/src/workspaces/emailer/src/transport.ts
+++ b/src/workspaces/emailer/src/transport.ts
@@ -21,9 +21,6 @@ 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}`;
}
@@ -32,10 +29,9 @@ function buildTransportUrl(config: SmtpConfig): string {
return `${protocol}://${auth}${config.host}:${config.port ?? 587}`;
}
-type TransportResult = {
- transport: Transporter;
- from: string | null;
-};
+type SmtpTransport = { type: 'smtp'; transport: Transporter; from: string | null };
+type ResendTransport = { type: 'resend'; apiKey: string; from: string };
+export type TransportResult = SmtpTransport | ResendTransport;
export async function getTransport(): Promise {
try {
@@ -44,14 +40,20 @@ export async function getTransport(): Promise {
const settings = await file.json();
const smtp: SmtpConfig | undefined = settings.smtp;
if (smtp) {
+ const from = `${smtp.fromName} <${smtp.fromEmail}>`;
+
+ if (smtp.provider === 'resend') {
+ return { type: 'resend', apiKey: smtp.apiKey ?? '', from };
+ }
+
const hash = JSON.stringify(smtp);
if (cachedTransport && cachedConfigHash === hash) {
- return { transport: cachedTransport, from: `${smtp.fromName} <${smtp.fromEmail}>` };
+ return { type: 'smtp', transport: cachedTransport, from };
}
const url = buildTransportUrl(smtp);
cachedTransport = createTransport(url);
cachedConfigHash = hash;
- return { transport: cachedTransport, from: `${smtp.fromName} <${smtp.fromEmail}>` };
+ return { type: 'smtp', transport: cachedTransport, from };
}
}
} catch {
@@ -63,7 +65,7 @@ export async function getTransport(): Promise {
cachedTransport = createTransport(MAIL_TRANSPORT);
cachedConfigHash = 'env';
}
- return { transport: cachedTransport, from: null };
+ return { type: 'smtp', transport: cachedTransport, from: null };
}
throw new Error('No email transport configured');