mirror of
https://github.com/Crazyco-xyz/48hr.email.git
synced 2026-01-09 11:19:36 +01:00
Uses seperate SMTP credentials for forwarding. This is just the raw system, validation will be in the next commit.
222 lines
6.8 KiB
JavaScript
222 lines
6.8 KiB
JavaScript
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) {
|
|
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)
|
|
|
|
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
|
|
* @returns {Object} - Nodemailer message object
|
|
* @private
|
|
*/
|
|
_buildForwardMessage(mail, destinationEmail) {
|
|
// 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: this.config.smtp.fromName,
|
|
address: this.config.smtp.user
|
|
},
|
|
to: destinationEmail,
|
|
subject: `Fwd: ${originalSubject}`,
|
|
text: forwardedBody,
|
|
replyTo: originalFrom
|
|
}
|
|
|
|
// Add HTML body if available
|
|
if (mail.html) {
|
|
const htmlForwardedBody = `
|
|
<div style="border-left: 2px solid #ccc; padding-left: 10px; margin: 10px 0;">
|
|
<p><strong>---------- Forwarded message ----------</strong><br>
|
|
<strong>From:</strong> ${this._escapeHtml(originalFrom)}<br>
|
|
<strong>Date:</strong> ${this._escapeHtml(originalDate)}<br>
|
|
<strong>Subject:</strong> ${this._escapeHtml(originalSubject)}<br>
|
|
<strong>To:</strong> ${this._escapeHtml(originalTo)}</p>
|
|
</div>
|
|
${mail.html}
|
|
`
|
|
message.html = htmlForwardedBody
|
|
}
|
|
|
|
// Add attachments if present
|
|
if (mail.attachments && mail.attachments.length > 0) {
|
|
message.attachments = mail.attachments.map(att => ({
|
|
filename: att.filename || 'attachment',
|
|
content: att.content,
|
|
contentType: att.contentType,
|
|
contentDisposition: att.contentDisposition || 'attachment'
|
|
}))
|
|
|
|
debug(`Including ${mail.attachments.length} attachment(s) in forwarded email`)
|
|
}
|
|
|
|
return message
|
|
}
|
|
|
|
/**
|
|
* Simple HTML escape for email headers
|
|
* @param {string} text
|
|
* @returns {string}
|
|
* @private
|
|
*/
|
|
_escapeHtml(text) {
|
|
if (!text) return ''
|
|
return String(text)
|
|
.replace(/&/g, '&')
|
|
.replace(/</g, '<')
|
|
.replace(/>/g, '>')
|
|
.replace(/"/g, '"')
|
|
.replace(/'/g, ''')
|
|
}
|
|
|
|
/**
|
|
* Verify SMTP connection
|
|
* @returns {Promise<{success: boolean, error?: string}>}
|
|
*/
|
|
async verifyConnection() {
|
|
if (!this.transporter) {
|
|
return {
|
|
success: false,
|
|
error: 'SMTP is not configured'
|
|
}
|
|
}
|
|
|
|
try {
|
|
await this.transporter.verify()
|
|
debug('SMTP connection verified successfully')
|
|
return { success: true }
|
|
} catch (error) {
|
|
debug('SMTP connection verification failed:', error.message)
|
|
return {
|
|
success: false,
|
|
error: error.message
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = SmtpService
|