From ddf1cfd01a06ae78e0ceafecf105192d61d5655d Mon Sep 17 00:00:00 2001 From: Konstantin Wohlwend Date: Mon, 28 Jul 2025 17:27:21 -0700 Subject: [PATCH] Improve email deliverability checks --- apps/backend/src/lib/config.tsx | 2 +- apps/backend/src/lib/emails.tsx | 22 +++++++++++++++++---- apps/dashboard/src/app/background-shine.tsx | 4 ++-- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/apps/backend/src/lib/config.tsx b/apps/backend/src/lib/config.tsx index 73c879ef5..7fde2e4e8 100644 --- a/apps/backend/src/lib/config.tsx +++ b/apps/backend/src/lib/config.tsx @@ -114,7 +114,7 @@ export function getProjectConfigOverrideQuery(options: ProjectOptions): RawQuery throw new StackAssertionError(`Expected 0 or 1 project config overrides for project ${options.projectId}, got ${queryResult.length}`, { queryResult }); } if (queryResult.length === 0) { - throw new StackAssertionError(`Expected 1 project config override for project ${options.projectId}, got 0`, { queryResult, options }); + throw new StackAssertionError(`Expected a project row for project ${options.projectId}, got 0`, { queryResult, options }); } return queryResult[0].projectConfigOverride ?? {}; }, diff --git a/apps/backend/src/lib/emails.tsx b/apps/backend/src/lib/emails.tsx index a6e4e05ac..7bf88d034 100644 --- a/apps/backend/src/lib/emails.tsx +++ b/apps/backend/src/lib/emails.tsx @@ -59,9 +59,9 @@ async function _sendEmailWithoutRetries(options: SendEmailOptions): Promise> { let finished = false; runAsynchronously(async () => { - await wait(5000); + await wait(10000); if (!finished) { - captureError("email-send-timeout", new StackAssertionError("Email send took longer than 5s; maybe the email service is too slow?", { + captureError("email-send-timeout", new StackAssertionError("Email send took longer than 10s; maybe the email service is too slow?", { config: options.emailConfig.type === 'shared' ? "shared" : pick(options.emailConfig, ['host', 'port', 'username', 'senderEmail', 'senderName']), to: options.to, subject: options.subject, @@ -78,7 +78,20 @@ async function _sendEmailWithoutRetries(options: SendEmailOptions): Promise { toArray = (await Promise.all(toArray.map(async (to) => { - const emailableResponse = await fetch(`https://api.emailable.com/v1/verify?email=${encodeURIComponent(options.to as string)}&api_key=${emailableApiKey}`); + const emailableResponseResult = await Result.retry(async (attempt) => { + const res = await fetch(`https://api.emailable.com/v1/verify?email=${encodeURIComponent(options.to as string)}&api_key=${emailableApiKey}`); + if (res.status === 249) { + const text = await res.text(); + console.log('Emailable is taking longer than expected, retrying...', text, { to: options.to }); + return Result.error(new Error("Emailable API returned a 249 error for " + options.to + ". This means it takes some more time to verify the email address. Response body: " + text)); + } + return Result.ok(res); + }, 4, { exponentialDelayBase: 4000 }); + if (emailableResponseResult.status === 'error') { + captureError("emailable-api-timeout", emailableResponseResult.error); + return to; + } + const emailableResponse = emailableResponseResult.data; if (!emailableResponse.ok) { throw new StackAssertionError("Failed to verify email address with Emailable", { to: options.to, @@ -87,7 +100,8 @@ async function _sendEmailWithoutRetries(options: SendEmailOptions): Promise