fix(email): test saved SMTP config and add TLS modes (#3388)

This commit is contained in:
Hampus authored and GitHub committed 2026-10-11 13:30:00 +02:00
1 parent 45b78a13c2
commit cca8227ca6
66 files changed
+2464 -319

No files matched your search

+1
View File
@@ -12,6 +12,7 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@fluxer/constants": "workspace:*",
"@fluxer/i18n": "workspace:*",
"@fluxer/logger": "workspace:*",
"itty-time": "catalog:",
@@ -0,0 +1,28 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import {SmtpEmailProvider} from '@pkgs/email/src/SmtpEmailProvider';
import {beforeEach, describe, expect, it, vi} from 'vitest';
const {createTransport} = vi.hoisted(() => ({createTransport: vi.fn()}));
vi.mock('nodemailer', () => ({default: {createTransport}}));
describe('SmtpEmailProvider', () => {
beforeEach(() => {
createTransport.mockReset();
createTransport.mockReturnValue({sendMail: vi.fn(), verify: vi.fn()});
});
it.each([
{port: 465, tlsMode: undefined, secure: true, requireTLS: false, ignoreTLS: false},
{port: 587, tlsMode: undefined, secure: false, requireTLS: true, ignoreTLS: false},
{port: 587, tlsMode: null, secure: false, requireTLS: true, ignoreTLS: false},
{port: 587, tlsMode: 'implicit', secure: true, requireTLS: false, ignoreTLS: false},
{port: 465, tlsMode: 'starttls', secure: false, requireTLS: true, ignoreTLS: false},
{port: 25, tlsMode: 'opportunistic', secure: false, requireTLS: false, ignoreTLS: false},
{port: 587, tlsMode: 'none', secure: false, requireTLS: false, ignoreTLS: true},
] as const)('maps TLS mode $tlsMode on port $port to nodemailer options', ({port, tlsMode, ...expected}) => {
new SmtpEmailProvider({host: 'smtp.example.com', port, username: 'user', password: 'pass', tlsMode});
expect(createTransport).toHaveBeenCalledWith(expect.objectContaining(expected));
});
});
+12 -3
View File
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import {resolveSmtpTlsMode, type SmtpTlsMode, SmtpTlsModes} from '@fluxer/constants/src/SmtpConstants';
import {createLogger} from '@fluxer/logger/src/Logger';
import type {EmailMessage, IEmailProvider} from '@pkgs/email/src/EmailProviderTypes';
import nodemailer, {type Transporter} from 'nodemailer';
@@ -11,7 +12,7 @@ interface SmtpEmailConfig {
port: number;
username: string;
password: string;
secure?: boolean;
tlsMode?: SmtpTlsMode | null;
connectionTimeoutMs?: number;
greetingTimeoutMs?: number;
socketTimeoutMs?: number;
@@ -19,12 +20,20 @@ interface SmtpEmailConfig {
export class SmtpEmailProvider implements IEmailProvider {
private readonly transporter: Transporter;
private readonly host: string;
private readonly port: number;
private readonly tlsMode: SmtpTlsMode;
constructor(config: SmtpEmailConfig) {
this.host = config.host;
this.port = config.port;
this.tlsMode = resolveSmtpTlsMode(config.tlsMode, config.port);
this.transporter = nodemailer.createTransport({
host: config.host,
port: config.port,
secure: config.secure ?? true,
secure: this.tlsMode === SmtpTlsModes.IMPLICIT,
requireTLS: this.tlsMode === SmtpTlsModes.STARTTLS,
ignoreTLS: this.tlsMode === SmtpTlsModes.NONE,
auth: {
user: config.username,
pass: config.password,
@@ -52,7 +61,7 @@ export class SmtpEmailProvider implements IEmailProvider {
logger.debug({to: message.to}, 'Email sent via SMTP');
return true;
} catch (error) {
logger.error({error}, 'SMTP send failed');
logger.error({error, host: this.host, port: this.port, tlsMode: this.tlsMode}, 'SMTP send failed');
return false;
}
}