diff --git a/examples/nodemailer-transport.ts b/examples/nodemailer-transport.ts index 1c3f425..e212af1 100644 --- a/examples/nodemailer-transport.ts +++ b/examples/nodemailer-transport.ts @@ -16,7 +16,7 @@ async function nodemailerTransportExample() { apiKey: 'cebc9a7f-4e0c-4fda-9dd0-85f48c02800c', port: 80, secure: false, // Set to true for wss:// - debug: false + debug: true }); // Create Nodemailer transporter @@ -51,7 +51,15 @@ async function nodemailerTransportExample() {
  • Nodemailer compatibility
  • WebSocket-based SMTP relay
  • - ` +

    This email includes a test attachment!

    + `, + attachments: [ + { + filename: 'test-attachment.txt', + content: Buffer.from(`This is a test attachment generated at ${new Date().toISOString()}\n\nRandom data: ${Math.random()}\nUUID: ${Date.now()}-${Math.random().toString(36).substr(2, 9)}\n\nFeatures tested:\n- File attachment support\n- Binary content handling\n- MIME multipart encoding\n- WebSocket SMTP transport\n\nEnd of test file.`, 'utf8'), + contentType: 'text/plain' + } + ] }); console.log('Email sent successfully!'); diff --git a/src/transport.ts b/src/transport.ts index f59e786..8ca72f6 100644 --- a/src/transport.ts +++ b/src/transport.ts @@ -6,6 +6,7 @@ import { EventEmitter } from 'events'; import { SMTPOverWSClient } from './client'; import { SMTPClientConfig, ConnectionState } from './types'; import { ConnectionError, MessageError, TimeoutError } from './errors'; +import { Readable } from 'stream'; /** * Nodemailer transport interface compatibility @@ -45,7 +46,8 @@ export interface MailMessage { data: any; message: { _envelope: Envelope; - _raw: string | Buffer; + _raw?: string | Buffer; + createReadStream(): Readable; }; mailer: any; } @@ -202,9 +204,11 @@ export class SMTPWSTransport extends EventEmitter { */ private async sendMail(mail: MailMessage): Promise { const envelope = this.extractEnvelope(mail); - const raw = mail.message._raw; const messageId = this.generateMessageId(); + // Get the raw message content from Nodemailer's stream + const rawMessage = await this.getRawMessage(mail); + // Build complete SMTP transaction let smtpTransaction = ''; @@ -220,7 +224,7 @@ export class SMTPWSTransport extends EventEmitter { smtpTransaction += 'DATA\r\n'; // Message content - const messageData = this.prepareMessageData(raw); + const messageData = this.prepareMessageData(rawMessage); smtpTransaction += messageData; // QUIT @@ -337,6 +341,36 @@ export class SMTPWSTransport extends EventEmitter { return `smtp-ws-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; } + /** + * Get raw message content from Nodemailer mail object + */ + private async getRawMessage(mail: MailMessage): Promise { + return new Promise((resolve, reject) => { + // If raw message is already available, use it + if (mail.message._raw) { + resolve(mail.message._raw.toString()); + return; + } + + // Otherwise, stream the message from Nodemailer + const chunks: Buffer[] = []; + const stream = mail.message.createReadStream(); + + stream.on('data', (chunk: Buffer) => { + chunks.push(chunk); + }); + + stream.on('end', () => { + const rawMessage = Buffer.concat(chunks).toString(); + resolve(rawMessage); + }); + + stream.on('error', (error) => { + reject(new MessageError(`Failed to read message stream: ${error.message}`, 'stream-read', 0)); + }); + }); + } + } /**