Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added custom SMTP email server testing on the dashboard #376

Merged
merged 15 commits into from
Dec 20, 2024
Merged
Prev Previous commit
Next Next commit
renamed retryable
fomalhautb committed Dec 20, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
commit 550169009be9678d99a42dee8397f208db05967e
1 change: 0 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -56,7 +56,6 @@
"quetzallabs",
"rehype",
"reqs",
"retryable",
"RPID",
"simplewebauthn",
"spoofable",
36 changes: 18 additions & 18 deletions apps/backend/src/lib/emails.tsx
Original file line number Diff line number Diff line change
@@ -73,7 +73,7 @@ type SendEmailOptions = {
export async function sendEmailWithKnownErrorTypes(options: SendEmailOptions): Promise<Result<undefined, {
rawError: any,
errorType: 'UNKNOWN' | 'HOST_NOT_FOUND' | 'AUTH_FAILED' | 'SOCKET_CLOSED' | 'TEMPORARY' | 'INVALID_EMAIL_ADDRESS',
retryable: boolean,
canRetry: boolean,
message?: string,
}>> {
try {
@@ -110,7 +110,7 @@ export async function sendEmailWithKnownErrorTypes(options: SendEmailOptions): P
return Result.error({
rawError: error,
errorType: 'HOST_NOT_FOUND',
retryable: false,
canRetry: false,
message: 'Failed to connect to the email host. Please make sure the email host configuration is correct.'
});
}
@@ -119,7 +119,7 @@ export async function sendEmailWithKnownErrorTypes(options: SendEmailOptions): P
return Result.error({
rawError: error,
errorType: 'AUTH_FAILED',
retryable: false,
canRetry: false,
message: 'Failed to authenticate with the email server. Please check your email credentials configuration.',
});
}
@@ -128,7 +128,7 @@ export async function sendEmailWithKnownErrorTypes(options: SendEmailOptions): P
return Result.error({
rawError: error,
errorType: 'TEMPORARY',
retryable: true,
canRetry: true,
message: 'The email server returned a temporary error. This could be due to a temporary network issue or a temporary block on the email server. Please try again later.\n\nError: ' + getServerResponse(error),
});
}
@@ -137,7 +137,7 @@ export async function sendEmailWithKnownErrorTypes(options: SendEmailOptions): P
return Result.error({
rawError: error,
errorType: 'INVALID_EMAIL_ADDRESS',
retryable: false,
canRetry: false,
message: 'The email address provided is invalid. Please verify both the recipient and sender email addresses configuration are correct.\n\nError:' + getServerResponse(error),
});
}
@@ -146,7 +146,7 @@ export async function sendEmailWithKnownErrorTypes(options: SendEmailOptions): P
return Result.error({
rawError: error,
errorType: 'SOCKET_CLOSED',
retryable: false,
canRetry: false,
message: 'Connection to email server was lost unexpectedly. This could be due to incorrect email server port configuration or a temporary network issue. Please verify your configuration and try again.',
});
}
@@ -168,7 +168,7 @@ export async function sendEmailWithKnownErrorTypes(options: SendEmailOptions): P
return Result.error({
rawError: error,
errorType: 'UNKNOWN',
retryable: true,
canRetry: true,
message: 'Failed to send email, but error is possibly transient due to the internet connection. Please check your email configuration and try again later.',
});
}
@@ -177,28 +177,28 @@ export async function sendEmailWithKnownErrorTypes(options: SendEmailOptions): P
return Result.error({
rawError: error,
errorType: 'UNKNOWN',
retryable: false,
canRetry: false,
message: 'An unknown error occurred while sending the email.',
});
}
}

export async function sendEmail({
emailConfig,
to,
subject,
text,
html,
}: SendEmailOptions) {
export async function sendEmail(options: SendEmailOptions) {
await trace.getTracer('stackframe').startActiveSpan('sendEmail', async (span) => {
try {
return Result.orThrow(await Result.retry(async (attempt) => {
const result = await sendEmailWithKnownErrorTypes({ emailConfig, to, subject, text, html });
const result = await sendEmailWithKnownErrorTypes(options);

if (result.status === 'error') {
const extraData = { host: emailConfig.host, from: emailConfig.senderEmail, to, subject, cause: result.error.rawError };
const extraData = {
host: options.emailConfig.host,
from: options.emailConfig.senderEmail,
to: options.to,
subject: options.subject,
cause: result.error.rawError,
};

if (result.error.retryable) {
if (result.error.canRetry) {
console.warn("Failed to send email, but error is possibly transient so retrying.", extraData, result.error.rawError);
return Result.error(result.error);
}