feat(server): added smtp client support for emailing (#306)

This commit is contained in:
Corentin Thomasset
2025-05-25 11:47:12 +02:00
committed by GitHub
parent cb38d66485
commit f0876fdc63
7 changed files with 120 additions and 1 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"@papra/app-server": minor
---
Added support for classic SMTP client for email sending
+2
View File
@@ -57,6 +57,7 @@
"mime-types": "^3.0.1",
"nanoid": "^5.1.5",
"node-cron": "^3.0.3",
"nodemailer": "^7.0.3",
"p-limit": "^6.2.0",
"p-queue": "^8.1.0",
"picomatch": "^4.0.2",
@@ -76,6 +77,7 @@
"@types/mime-types": "^2.1.4",
"@types/node": "^22.10.2",
"@types/node-cron": "^3.0.11",
"@types/nodemailer": "^6.4.17",
"@types/picomatch": "^4.0.0",
"@types/sanitize-html": "^2.16.0",
"@vitest/coverage-v8": "catalog:",
@@ -1,9 +1,11 @@
import { LOGGER_EMAIL_DRIVER_NAME, loggerEmailDriverFactory } from './logger/logger.email-driver';
import { RESEND_EMAIL_DRIVER_NAME, resendEmailDriverFactory } from './resend/resend.email-driver';
import { SMTP_EMAIL_DRIVER_NAME, smtpEmailDriverFactory } from './smtp/smtp.email-driver';
export const emailDrivers = {
[RESEND_EMAIL_DRIVER_NAME]: resendEmailDriverFactory,
[LOGGER_EMAIL_DRIVER_NAME]: loggerEmailDriverFactory,
[SMTP_EMAIL_DRIVER_NAME]: smtpEmailDriverFactory,
} as const;
export const emailDriverFactoryNames = Object.keys(emailDrivers);
@@ -0,0 +1,43 @@
import type { ConfigDefinition } from 'figue';
import { z } from 'zod';
import { booleanishSchema } from '../../../config/config.schemas';
import { parseJson } from '../../../intake-emails/intake-emails.schemas';
export const smtpEmailDriverConfig = {
host: {
doc: 'The host of the SMTP server',
schema: z.string().optional(),
default: '',
env: 'SMTP_HOST',
},
port: {
doc: 'The port of the SMTP server',
schema: z.coerce.number(),
default: 587,
env: 'SMTP_PORT',
},
user: {
doc: 'The user of the SMTP server',
schema: z.string().optional(),
default: undefined,
env: 'SMTP_USER',
},
password: {
doc: 'The password of the SMTP server',
schema: z.string().optional(),
default: undefined,
env: 'SMTP_PASSWORD',
},
secure: {
doc: 'Whether to use a secure connection to the SMTP server',
schema: booleanishSchema,
default: false,
env: 'SMTP_SECURE',
},
rawConfig: {
doc: 'The raw configuration for the nodemailer SMTP client in JSON format for advanced use cases. If set, this will override all other config options. See https://nodemailer.com/smtp/ for more details.',
schema: z.string().transform(parseJson).optional(),
default: undefined,
env: 'SMTP_JSON_CONFIG',
},
} as const satisfies ConfigDefinition;
@@ -0,0 +1,45 @@
import nodemailer from 'nodemailer';
import { createError } from '../../../shared/errors/errors';
import { defineEmailDriverFactory } from '../email-driver.models';
export const SMTP_EMAIL_DRIVER_NAME = 'smtp';
export const smtpEmailDriverFactory = defineEmailDriverFactory(({ config, logger }) => {
const { fromEmail } = config.emails;
const { host, port, secure, user, password, rawConfig } = config.emails.drivers.smtp;
const transporter = nodemailer.createTransport(rawConfig ?? {
host,
port,
secure,
auth: {
user,
pass: password,
},
});
return {
name: SMTP_EMAIL_DRIVER_NAME,
sendEmail: async ({ to, subject, html, from }) => {
try {
const { messageId } = await transporter.sendMail({
from: from ?? fromEmail,
to,
subject,
html,
});
logger.info({ messageId }, 'Email sent');
} catch (error) {
logger.error({ error }, 'Failed to send email');
throw createError({
code: 'email.send_failed',
message: 'Failed to send email',
statusCode: 500,
isInternal: true,
});
}
},
};
});
@@ -4,6 +4,7 @@ import { emailDriverFactoryNames } from './drivers/email-driver';
import { LOGGER_EMAIL_DRIVER_NAME } from './drivers/logger/logger.email-driver';
import { loggerEmailDriverConfig } from './drivers/logger/logger.email-driver.config';
import { resendEmailDriverConfig } from './drivers/resend/resend.email-driver.config';
import { smtpEmailDriverConfig } from './drivers/smtp/smtp.email-driver.config';
export const emailsConfig = {
fromEmail: {
@@ -21,5 +22,6 @@ export const emailsConfig = {
drivers: {
resend: resendEmailDriverConfig,
logger: loggerEmailDriverConfig,
smtp: smtpEmailDriverConfig,
},
} as const satisfies ConfigDefinition;
+21 -1
View File
@@ -319,6 +319,9 @@ importers:
node-cron:
specifier: ^3.0.3
version: 3.0.3
nodemailer:
specifier: ^7.0.3
version: 7.0.3
p-limit:
specifier: ^6.2.0
version: 6.2.0
@@ -371,6 +374,9 @@ importers:
'@types/node-cron':
specifier: ^3.0.11
version: 3.0.11
'@types/nodemailer':
specifier: ^6.4.17
version: 6.4.17
'@types/picomatch':
specifier: ^4.0.0
version: 4.0.0
@@ -3341,6 +3347,9 @@ packages:
'@types/node@22.15.18':
resolution: {integrity: sha512-v1DKRfUdyW+jJhZNEI1PYy29S2YRxMV5AOO/x/SjKmW0acCIOqmbj6Haf9eHAhsPmrhlHSxEhv/1WszcLWV4cg==}
'@types/nodemailer@6.4.17':
resolution: {integrity: sha512-I9CCaIp6DTldEg7vyUTZi8+9Vo0hi1/T8gv3C89yk1rSAAzoKQ8H8ki/jBYJSFoH/BisgLP8tkZMlQ91CIquww==}
'@types/normalize-package-data@2.4.4':
resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==}
@@ -5806,6 +5815,7 @@ packages:
libsql@0.4.7:
resolution: {integrity: sha512-T9eIRCs6b0J1SHKYIvD8+KCJMcWZ900iZyxdnSCdqxN12Z1ijzT+jY5nrk72Jw4B0HGzms2NgpryArlJqvc3Lw==}
cpu: [x64, arm64, wasm32]
os: [darwin, linux, win32]
lilconfig@3.1.3:
@@ -6292,6 +6302,10 @@ packages:
node-releases@2.0.19:
resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==}
nodemailer@7.0.3:
resolution: {integrity: sha512-Ajq6Sz1x7cIK3pN6KesGTah+1gnwMnx5gKl3piQlQQE/PwyJ4Mbc8is2psWYxK3RJTVeqsDaCv8ZzXLCDHMTZw==}
engines: {node: '>=6.0.0'}
nopt@5.0.0:
resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==}
engines: {node: '>=6'}
@@ -11420,6 +11434,10 @@ snapshots:
dependencies:
undici-types: 6.21.0
'@types/nodemailer@6.4.17':
dependencies:
'@types/node': 22.15.18
'@types/normalize-package-data@2.4.4': {}
'@types/picomatch@3.0.2': {}
@@ -11434,7 +11452,7 @@ snapshots:
'@types/sax@1.2.7':
dependencies:
'@types/node': 17.0.45
'@types/node': 22.15.18
'@types/unist@2.0.11': {}
@@ -15340,6 +15358,8 @@ snapshots:
node-releases@2.0.19: {}
nodemailer@7.0.3: {}
nopt@5.0.0:
dependencies:
abbrev: 1.1.1