const nodemailer = require('nodemailer')
const debug = require('debug')('48hr-email:smtp-service')
/**
* SMTP Service for forwarding emails
* Uses nodemailer to send forwarded emails via configured SMTP server
*/
class SmtpService {
constructor(config) {
this.config = config
this.transporter = null
// Only initialize transporter if SMTP is configured
if (this._isConfigured()) {
this._initializeTransporter()
} else {
debug('SMTP not configured - forwarding functionality will be unavailable')
}
}
/**
* Check if SMTP is properly configured
* @returns {boolean}
*/
_isConfigured() {
return !!(
this.config.smtp.enabled &&
this.config.smtp.host &&
this.config.smtp.user &&
this.config.smtp.password
)
}
/**
* Initialize the nodemailer transporter
* @private
*/
_initializeTransporter() {
try {
this.transporter = nodemailer.createTransport({
host: this.config.smtp.host,
port: this.config.smtp.port,
secure: this.config.smtp.secure,
auth: {
user: this.config.smtp.user,
pass: this.config.smtp.password
},
tls: {
// Allow self-signed certificates and skip verification
// This is useful for development or internal SMTP servers
rejectUnauthorized: false
}
})
debug(`SMTP transporter initialized: ${this.config.smtp.host}:${this.config.smtp.port}`)
} catch (error) {
debug('Failed to initialize SMTP transporter:', error.message)
throw new Error(`SMTP initialization failed: ${error.message}`)
}
}
/**
* Forward an email to a destination address
* @param {Object} mail - Parsed email object from mailparser
* @param {string} destinationEmail - Email address to forward to
* @returns {Promise<{success: boolean, error?: string, messageId?: string}>}
*/
async forwardMail(mail, destinationEmail, branding = '48hr.email') {
if (!this.transporter) {
return {
success: false,
error: 'SMTP is not configured. Please configure SMTP settings to enable forwarding.'
}
}
if (!mail) {
return {
success: false,
error: 'Email not found'
}
}
try {
debug(`Forwarding email (Subject: "${mail.subject}") to ${destinationEmail}`)
const forwardMessage = this._buildForwardMessage(mail, destinationEmail, branding)
const info = await this.transporter.sendMail(forwardMessage)
debug(`Email forwarded successfully. MessageId: ${info.messageId}`)
return {
success: true,
messageId: info.messageId
}
} catch (error) {
debug('Failed to forward email:', error.message)
return {
success: false,
error: `Failed to send email: ${error.message}`
}
}
}
/**
* Build the forward message structure
* @param {Object} mail - Parsed email object
* @param {string} destinationEmail - Destination address
* @param {string} branding - Service branding name
* @returns {Object} - Nodemailer message object
* @private
*/
_buildForwardMessage(mail, destinationEmail, branding = '48hr.email') {
// Extract original sender info
const originalFrom = (mail.from && mail.from.text) || 'Unknown Sender'
const originalTo = (mail.to && mail.to.text) || 'Unknown Recipient'
const originalDate = mail.date ? new Date(mail.date).toLocaleString() : 'Unknown Date'
const originalSubject = mail.subject || '(no subject)'
// Build forwarded message body
let forwardedBody = `
---------- Forwarded message ----------
From: ${originalFrom}
Date: ${originalDate}
Subject: ${originalSubject}
To: ${originalTo}
`
// Add original text body if available
if (mail.text) {
forwardedBody += mail.text
} else if (mail.html) {
// If only HTML is available, mention it
forwardedBody += '[This email contains HTML content. See attachment or HTML version below.]\n\n'
}
// Build the message object
const message = {
from: {
name: branding,
address: this.config.smtp.user
},
to: destinationEmail,
subject: `Fwd: ${originalSubject}`,
text: forwardedBody,
replyTo: originalFrom
}
// Add HTML body if available
if (mail.html) {
const htmlForwardedBody = `
Hello,
You requested to use ${this._escapeHtml(destinationEmail)} as a forwarding destination on ${this._escapeHtml(branding)}.
To verify ownership of this email address and enable forwarding for 24 hours, please click the button below:
Or copy and paste this link into your browser:
${verificationLink}
⚠️ Important: This verification link expires in 15 minutes. Once verified, you'll be able to forward emails to this address for 24 hours.
If you didn't request this verification, you can safely ignore this email.
`
const textContent = `
Verify Your Email Address
You requested to use ${destinationEmail} as a forwarding destination on ${branding}.
To verify ownership of this email address and enable forwarding for 24 hours, please visit:
${verificationLink}
IMPORTANT: This verification link expires in 15 minutes. Once verified, you'll be able to forward emails to this address for 24 hours.
If you didn't request this verification, you can safely ignore this email.
---
This is an automated message from ${branding}
`
try {
const info = await this.transporter.sendMail({
from: `"${branding} Forwarding Service" <${this.config.smtp.user}>`,
to: destinationEmail,
subject: `${branding} - Verify your email for forwarding`,
text: textContent,
html: htmlContent
})
debug(`Verification email sent to ${destinationEmail}, messageId: ${info.messageId}`)
return {
success: true,
messageId: info.messageId
}
} catch (error) {
debug(`Failed to send verification email: ${error.message}`)
return {
success: false,
error: `Failed to send verification email: ${error.message}`
}
}
}
}
module.exports = SmtpService