diff --git a/examples/bulk-email.ts b/examples/bulk-email.ts deleted file mode 100644 index 054594e..0000000 --- a/examples/bulk-email.ts +++ /dev/null @@ -1,210 +0,0 @@ -#!/usr/bin/env ts-node - -/** - * Bulk email sending example using Nodemailer transport - */ - -import nodemailer from 'nodemailer'; -import { createTransport } from '../src/index'; - -async function bulkEmailExample() { - console.log('Bulk Email Example using SMTP WebSocket Transport\n'); - - // Create the WebSocket transport - const transport = createTransport({ - host: 'localhost', - port: 3000, - auth: { - user: 'your-api-key-here' - }, - maxQueueSize: 1000, // Handle large queues - debug: false // Disable debug for bulk operations - }); - - const transporter = nodemailer.createTransporter(transport); - - // Sample recipient list - const recipients = [ - { email: 'user1@example.com', name: 'User One' }, - { email: 'user2@example.com', name: 'User Two' }, - { email: 'user3@example.com', name: 'User Three' }, - { email: 'user4@example.com', name: 'User Four' }, - { email: 'user5@example.com', name: 'User Five' } - ]; - - console.log(`Sending emails to ${recipients.length} recipients...\n`); - - const results = []; - const startTime = Date.now(); - - // Send emails concurrently (transport handles queuing automatically) - const emailPromises = recipients.map(async (recipient, index) => { - try { - const info = await transporter.sendMail({ - from: 'newsletter@example.com', - to: recipient.email, - subject: `Newsletter #${index + 1} - ${new Date().toLocaleDateString()}`, - text: `Hello ${recipient.name}!\n\nThis is your personalized newsletter.\n\nBest regards,\nThe Newsletter Team`, - html: ` -
This is your personalized newsletter for ${new Date().toLocaleDateString()}.
-This email was delivered via our SMTP WebSocket transport system.
-Newsletter #${index + 1} | Sent at ${new Date().toLocaleTimeString()}
- ` - }); - - console.log(`Email ${index + 1}/${recipients.length} sent to ${recipient.email}`); - return { - success: true, - recipient: recipient.email, - messageId: info.messageId, - response: info.response - }; - - } catch (error) { - console.error(`Failed to send email ${index + 1} to ${recipient.email}:`, (error as Error).message); - return { - success: false, - recipient: recipient.email, - error: (error as Error).message - }; - } - }); - - // Wait for all emails to complete - const emailResults = await Promise.allSettled(emailPromises); - const duration = Date.now() - startTime; - - // Process results - let successful = 0; - let failed = 0; - - emailResults.forEach((result) => { - if (result.status === 'fulfilled') { - results.push(result.value); - if (result.value.success) { - successful++; - } else { - failed++; - } - } else { - failed++; - results.push({ - success: false, - error: result.reason.message - }); - } - }); - - // Display summary - console.log('\n--- Bulk Email Results ---'); - console.log(`Total emails: ${recipients.length}`); - console.log(`Successful: ${successful}`); - console.log(`Failed: ${failed}`); - console.log(`Duration: ${(duration / 1000).toFixed(2)} seconds`); - console.log(`Average time per email: ${(duration / recipients.length).toFixed(0)}ms`); - - // Display failed emails if any - if (failed > 0) { - console.log('\nFailed emails:'); - results.forEach((result, index) => { - if (!result.success) { - console.log(` ${index + 1}. ${result.recipient || 'Unknown'}: ${result.error}`); - } - }); - } - - // Close transport - await transport.close(); - console.log('\nTransport closed'); -} - -// Advanced bulk email with throttling -async function throttledBulkEmail() { - console.log('\nThrottled Bulk Email Example\n'); - - const transport = createTransport({ - host: 'localhost', - port: 3000, - auth: { - user: 'your-api-key-here' - } - }); - - const transporter = nodemailer.createTransporter(transport); - - // Generate larger recipient list - const recipients = Array.from({ length: 20 }, (_, i) => ({ - email: `user${i + 1}@example.com`, - name: `User ${i + 1}` - })); - - console.log(`Sending throttled emails to ${recipients.length} recipients...`); - console.log('Processing 5 emails at a time with 1 second delay between batches\n'); - - const batchSize = 5; - const batches = []; - - for (let i = 0; i < recipients.length; i += batchSize) { - batches.push(recipients.slice(i, i + batchSize)); - } - - let totalSuccessful = 0; - let totalFailed = 0; - - for (let batchIndex = 0; batchIndex < batches.length; batchIndex++) { - const batch = batches[batchIndex]; - console.log(`Processing batch ${batchIndex + 1}/${batches.length} (${batch.length} emails)...`); - - const batchPromises = batch.map(async (recipient) => { - try { - await transporter.sendMail({ - from: 'batch@example.com', - to: recipient.email, - subject: `Batch Email - ${recipient.name}`, - text: `Hello ${recipient.name}, this is a batch email.` - }); - return { success: true, email: recipient.email }; - } catch (error) { - return { success: false, email: recipient.email, error: (error as Error).message }; - } - }); - - const batchResults = await Promise.all(batchPromises); - - const batchSuccessful = batchResults.filter(r => r.success).length; - const batchFailed = batchResults.filter(r => !r.success).length; - - totalSuccessful += batchSuccessful; - totalFailed += batchFailed; - - console.log(`Batch ${batchIndex + 1} complete: ${batchSuccessful} successful, ${batchFailed} failed`); - - // Wait between batches (except for the last one) - if (batchIndex < batches.length - 1) { - await new Promise(resolve => setTimeout(resolve, 1000)); - } - } - - console.log(`\nThrottled bulk email complete: ${totalSuccessful} successful, ${totalFailed} failed`); - - await transport.close(); -} - -// Run the examples -if (require.main === module) { - (async () => { - try { - await bulkEmailExample(); - await throttledBulkEmail(); - console.log('\nBulk email examples completed successfully'); - } catch (error) { - console.error('\nExamples failed:', error); - } finally { - process.exit(0); - } - })(); -} - -export { bulkEmailExample, throttledBulkEmail }; \ No newline at end of file diff --git a/examples/nodemailer-transport.ts b/examples/nodemailer-transport.ts index ef00c47..1c3f425 100644 --- a/examples/nodemailer-transport.ts +++ b/examples/nodemailer-transport.ts @@ -13,12 +13,10 @@ async function nodemailerTransportExample() { // Create the WebSocket transport const transport = createTransport({ host: '192.168.0.62', + apiKey: 'cebc9a7f-4e0c-4fda-9dd0-85f48c02800c', port: 80, secure: false, // Set to true for wss:// - auth: { - user: 'cebc9a7f-4e0c-4fda-9dd0-85f48c02800c' // Your SMTP relay API key - }, - debug: true + debug: false }); // Create Nodemailer transporter @@ -39,8 +37,8 @@ async function nodemailerTransportExample() { console.log('Sending test email...'); const info = await transporter.sendMail({ - from: 'sender@example.com', - to: 'recipient@example.com', + from: 'cudconnex@satitm.chula.ac.th', + to: 'siwat.s@chula.ac.th', subject: 'Test Email via SMTP WebSocket', text: 'This email was sent using the SMTP WebSocket transport!', html: ` diff --git a/src/client.ts b/src/client.ts index ef574b6..806e214 100644 --- a/src/client.ts +++ b/src/client.ts @@ -532,23 +532,41 @@ export class SMTPOverWSClient extends EventEmitter { this.sendSMTPData('EHLO client\r\n'); // Wait for EHLO response - const onEhloResponse = (ehloMessage: SMTPFromServerMessage) => { - if (this.channelTimer) { - clearTimeout(this.channelTimer); - this.channelTimer = null; - } - + const onEhloResponse = async (ehloMessage: SMTPFromServerMessage) => { this.logger.debug('RX SMTP EHLO response', { response: ehloMessage.data.trim(), size: ehloMessage.data.length }); if (ehloMessage.data.startsWith('250')) { - this.setState(ConnectionState.CHANNEL_READY); - this.emit('channelOpened'); - this.logger.debug('SMTP channel ready after EHLO'); - resolve(); + try { + // Perform SMTP authentication + await this.performSMTPAuth(); + + if (this.channelTimer) { + clearTimeout(this.channelTimer); + this.channelTimer = null; + } + + this.setState(ConnectionState.CHANNEL_READY); + this.emit('channelOpened'); + this.logger.debug('SMTP channel ready after authentication'); + resolve(); + + } catch (authError) { + if (this.channelTimer) { + clearTimeout(this.channelTimer); + this.channelTimer = null; + } + const error = ErrorFactory.fromChannelFailure(`SMTP authentication failed: ${(authError as Error).message}`); + this.emit('channelError', error); + reject(error); + } } else { + if (this.channelTimer) { + clearTimeout(this.channelTimer); + this.channelTimer = null; + } const error = ErrorFactory.fromChannelFailure(`EHLO rejected: ${ehloMessage.data.trim()}`); this.emit('channelError', error); reject(error); @@ -936,6 +954,43 @@ export class SMTPOverWSClient extends EventEmitter { this.connectionStartTime = 0; } + /** + * Perform SMTP authentication using API key + */ + private async performSMTPAuth(): Promise