diff --git a/public/script.js b/public/script.js index a17a29a..9f9f53d 100644 --- a/public/script.js +++ b/public/script.js @@ -4,7 +4,7 @@ function togglePassword() { passInput.setAttribute('type', type); } -document.getElementById('smtpForm').addEventListener('submit', async function(e) { +document.getElementById('smtpForm').addEventListener('submit', async function (e) { e.preventDefault(); const btn = document.getElementById('testBtn'); @@ -19,7 +19,7 @@ document.getElementById('smtpForm').addEventListener('submit', async function(e) statusDiv.className = 'status-message'; statusDiv.textContent = ''; logOutput.textContent = ''; - + // Set Loading State btn.disabled = true; spinner.classList.remove('hidden'); @@ -37,6 +37,17 @@ document.getElementById('smtpForm').addEventListener('submit', async function(e) }, body: JSON.stringify(data), }); + if (!response.ok) { + // Handle non-200 responses + const contentType = response.headers.get("content-type"); + if (contentType && contentType.includes("application/json")) { + const errorResult = await response.json(); + throw new Error(errorResult.message || errorResult.error || "Server Error"); + } else { + const text = await response.text(); + throw new Error(`Server returned ${response.status}: ${text.substring(0, 100)}...`); + } + } const result = await response.json(); @@ -48,6 +59,7 @@ document.getElementById('smtpForm').addEventListener('submit', async function(e) statusDiv.textContent = '✅ Success! Email Sent Successfully.'; logOutput.textContent = JSON.stringify(result.details, null, 2); } else { + // This block handles cases where response was 200 OK but success is false (business logic error) statusDiv.classList.add('status-error'); statusDiv.textContent = '❌ Error: ' + result.message; logOutput.textContent = result.error || 'Unknown error occurred.'; @@ -56,8 +68,8 @@ document.getElementById('smtpForm').addEventListener('submit', async function(e) } catch (error) { resultsDiv.classList.remove('hidden'); statusDiv.classList.add('status-error'); - statusDiv.textContent = '❌ Network Error'; - logOutput.textContent = error.toString(); + statusDiv.textContent = '❌ Error Caught'; // Changed from "Network Error" to be more accurate + logOutput.textContent = error.message; // Use error.message instead of error.toString() } finally { // Reset Button btn.disabled = false; @@ -67,7 +79,7 @@ document.getElementById('smtpForm').addEventListener('submit', async function(e) }); // Auto Discovery Test Handler -document.getElementById('autoTestBtn').addEventListener('click', async function() { +document.getElementById('autoTestBtn').addEventListener('click', async function () { const btn = document.getElementById('autoTestBtn'); const spinner = btn.querySelector('.loading-spinner'); const btnText = btn.querySelector('.btn-text'); @@ -94,7 +106,7 @@ document.getElementById('autoTestBtn').addEventListener('click', async function( statusDiv.className = 'status-message'; statusDiv.textContent = ''; logOutput.textContent = ''; - + // Set Loading State btn.disabled = true; spinner.classList.remove('hidden'); @@ -108,6 +120,16 @@ document.getElementById('autoTestBtn').addEventListener('click', async function( }, body: JSON.stringify(autoTestData), }); + if (!response.ok) { + const contentType = response.headers.get("content-type"); + if (contentType && contentType.includes("application/json")) { + const errorResult = await response.json(); + throw new Error(errorResult.message || errorResult.error || "Server Error"); + } else { + const text = await response.text(); + throw new Error(`Server returned ${response.status}: ${text.substring(0, 100)}...`); + } + } const result = await response.json(); @@ -117,28 +139,28 @@ document.getElementById('autoTestBtn').addEventListener('click', async function( if (result.success) { statusDiv.classList.add('status-success'); statusDiv.textContent = `✅ ${result.message}`; - + // Format detailed results let detailedOutput = `Total Tests: ${result.totalTests}\n`; detailedOutput += `Successful: ${result.successfulConfigs}\n`; detailedOutput += `Failed: ${result.totalTests - result.successfulConfigs}\n\n`; detailedOutput += '─'.repeat(50) + '\n\n'; - + result.results.forEach((test, index) => { detailedOutput += `Test ${index + 1}: ${test.config}\n`; detailedOutput += ` Port: ${test.port}\n`; detailedOutput += ` Encryption: ${test.secure ? 'SSL/TLS' : 'STARTTLS'}\n`; detailedOutput += ` Status: ${test.status === 'success' ? '✅ SUCCESS' : '❌ FAILED'}\n`; - + if (test.status === 'success') { detailedOutput += ` Email Sent: ${test.messageId}\n`; } else { detailedOutput += ` Error: ${test.error}\n`; } - + detailedOutput += '\n'; }); - + logOutput.textContent = detailedOutput; } else { statusDiv.classList.add('status-error'); @@ -149,8 +171,8 @@ document.getElementById('autoTestBtn').addEventListener('click', async function( } catch (error) { resultsDiv.classList.remove('hidden'); statusDiv.classList.add('status-error'); - statusDiv.textContent = '❌ Network Error'; - logOutput.textContent = error.toString(); + statusDiv.textContent = '❌ Error Caught'; + logOutput.textContent = error.message; } finally { // Reset Button btn.disabled = false; diff --git a/server.js b/server.js index d5b87c4..df814bc 100644 --- a/server.js +++ b/server.js @@ -19,46 +19,45 @@ app.get('/', (req, res) => { }); app.post('/api/test-smtp', async (req, res) => { - const { host, port, secure, user, pass, from, to } = req.body; + const { host, port, secure, user, pass, from, to } = req.body; - // Log intent - console.log( - `Attempting SMTP connection to ${host}:${port} (${ - secure ? "Secure" : "Insecure" - }) for ${user}` - ); + // Log intent + console.log( + `Attempting SMTP connection to ${host}:${port} (${secure ? "Secure" : "Insecure" + }) for ${user}` + ); - // Create Transporter - const transporterConfig = { - host: host, - port: parseInt(port), - secure: secure === true || secure === "true", // true for 465, false for other ports - auth: { - user: user, - pass: pass, - }, - }; - - // Only add TLS settings if secure is not explicitly "none" - if (secure !== "none") { - transporterConfig.tls = { - rejectUnauthorized: false, // Allow self-signed certs for testing flexibility + // Create Transporter + const transporterConfig = { + host: host, + port: parseInt(port), + secure: secure === true || secure === "true", // true for 465, false for other ports + auth: { + user: user, + pass: pass, + }, }; - } - const transporter = nodemailer.createTransport(transporterConfig); + // Only add TLS settings if secure is not explicitly "none" + if (secure !== "none") { + transporterConfig.tls = { + rejectUnauthorized: false, // Allow self-signed certs for testing flexibility + }; + } - try { - // 1. Verify Connection - await transporter.verify(); - console.log("SMTP Connection Verified Successfully"); + try { + const transporter = nodemailer.createTransport(transporterConfig); - // 2. Send Test Email - const mailOptions = { - from: from || user, // Default to user if from not specified - to: to, - subject: "SMTP Test - Advanced SMTP Tester", - html: ` + // 1. Verify Connection + await transporter.verify(); + console.log("SMTP Connection Verified Successfully"); + + // 2. Send Test Email + const mailOptions = { + from: from || user, // Default to user if from not specified + to: to, + subject: "SMTP Test - Advanced SMTP Tester", + html: ` @@ -182,85 +181,85 @@ app.post('/api/test-smtp', async (req, res) => { `, - }; + }; - const info = await transporter.sendMail(mailOptions); - console.log("Message sent: %s", info.messageId); + const info = await transporter.sendMail(mailOptions); + console.log("Message sent: %s", info.messageId); - res.json({ - success: true, - message: "Connection verified and email sent successfully!", - details: { - messageId: info.messageId, - response: info.response, - }, - }); - } catch (error) { - console.error("SMTP Error:", error); - res.status(500).json({ - success: false, - message: "SMTP Test Failed", - error: error.message, - }); - } + res.json({ + success: true, + message: "Connection verified and email sent successfully!", + details: { + messageId: info.messageId, + response: info.response, + }, + }); + } catch (error) { + console.error("SMTP Error:", error); + res.status(500).json({ + success: false, + message: "SMTP Test Failed", + error: error.message, + }); + } }); // Auto-Discovery Endpoint - Tests multiple port/encryption combinations app.post('/api/auto-test-smtp', async (req, res) => { - const { host, user, pass, from, to } = req.body; + const { host, user, pass, from, to } = req.body; - // Common SMTP port/encryption combinations to test - const configurations = [ - { port: 587, secure: false, name: 'STARTTLS (587)', tls: true }, - { port: 465, secure: true, name: 'SSL/TLS (465)', tls: true }, - { port: 25, secure: false, name: 'STARTTLS (25)', tls: true }, - { port: 2525, secure: false, name: 'STARTTLS (2525)', tls: true }, - { port: 25, secure: false, name: 'Unencrypted (25)', tls: false }, - { port: 587, secure: false, name: 'Unencrypted (587)', tls: false }, - { port: 2525, secure: false, name: 'Unencrypted (2525)', tls: false }, - ]; + // Common SMTP port/encryption combinations to test + const configurations = [ + { port: 587, secure: false, name: 'STARTTLS (587)', tls: true }, + { port: 465, secure: true, name: 'SSL/TLS (465)', tls: true }, + { port: 25, secure: false, name: 'STARTTLS (25)', tls: true }, + { port: 2525, secure: false, name: 'STARTTLS (2525)', tls: true }, + { port: 25, secure: false, name: 'Unencrypted (25)', tls: false }, + { port: 587, secure: false, name: 'Unencrypted (587)', tls: false }, + { port: 2525, secure: false, name: 'Unencrypted (2525)', tls: false }, + ]; - console.log(`Starting auto-discovery for ${host} with user ${user}`); - - const results = []; - let successCount = 0; + console.log(`Starting auto-discovery for ${host} with user ${user}`); - // Test each configuration - for (const config of configurations) { - console.log(`Testing ${config.name}...`); - - const transporterConfig = { - host: host, - port: config.port, - secure: config.secure, - auth: { - user: user, - pass: pass, - }, - connectionTimeout: 10000, // 10 second timeout - greetingTimeout: 10000, - }; + const results = []; + let successCount = 0; - // Only add TLS settings if encryption is enabled - if (config.tls !== false) { - transporterConfig.tls = { - rejectUnauthorized: false, - }; - } + // Test each configuration + for (const config of configurations) { + console.log(`Testing ${config.name}...`); - const transporter = nodemailer.createTransport(transporterConfig); + const transporterConfig = { + host: host, + port: config.port, + secure: config.secure, + auth: { + user: user, + pass: pass, + }, + connectionTimeout: 10000, // 10 second timeout + greetingTimeout: 10000, + }; - try { - // Verify connection - await transporter.verify(); - console.log(`✅ ${config.name} - Connection successful!`); + // Only add TLS settings if encryption is enabled + if (config.tls !== false) { + transporterConfig.tls = { + rejectUnauthorized: false, + }; + } - // Send test email for this successful configuration - const mailOptions = { - from: from || user, - to: to, - subject: `SMTP Auto-Discovery: ${config.name} - SUCCESS`, - html: ` + try { + const transporter = nodemailer.createTransport(transporterConfig); + + // Verify connection + await transporter.verify(); + console.log(`✅ ${config.name} - Connection successful!`); + + // Send test email for this successful configuration + const mailOptions = { + from: from || user, + to: to, + subject: `SMTP Auto-Discovery: ${config.name} - SUCCESS`, + html: ` @@ -384,42 +383,52 @@ app.post('/api/auto-test-smtp', async (req, res) => { `, - }; + }; - const info = await transporter.sendMail(mailOptions); - successCount++; + const info = await transporter.sendMail(mailOptions); + successCount++; - results.push({ - config: config.name, - port: config.port, - secure: config.secure, - status: 'success', - messageId: info.messageId, - }); + results.push({ + config: config.name, + port: config.port, + secure: config.secure, + status: 'success', + messageId: info.messageId, + }); - console.log(`📧 Email sent for ${config.name}: ${info.messageId}`); - } catch (error) { - console.log(`❌ ${config.name} - Failed: ${error.message}`); - results.push({ - config: config.name, - port: config.port, - secure: config.secure, - status: 'failed', - error: error.message, - }); + console.log(`📧 Email sent for ${config.name}: ${info.messageId}`); + } catch (error) { + console.log(`❌ ${config.name} - Failed: ${error.message}`); + results.push({ + config: config.name, + port: config.port, + secure: config.secure, + status: 'failed', + error: error.message, + }); + } } - } - // Return summary of all tests - res.json({ - success: true, - message: `Auto-discovery complete. Found ${successCount} working configuration(s).`, - totalTests: configurations.length, - successfulConfigs: successCount, - results: results, - }); + // Return summary of all tests + res.json({ + success: true, + message: `Auto-discovery complete. Found ${successCount} working configuration(s).`, + totalTests: configurations.length, + successfulConfigs: successCount, + results: results, + }); +}); + +// Global Error Handler +app.use((err, req, res, next) => { + console.error("Unhandled Error:", err); + res.status(500).json({ + success: false, + message: "Internal Server Error", + error: err.message || "Unknown error occurred" + }); }); app.listen(port, () => { - console.log(`Server running at http://localhost:${port}`); + console.log(`Server running at http://localhost:${port}`); });