SMTP Config

This commit is contained in:
mukesh13
2025-08-26 13:52:37 +05:30
parent 4f6a0d83bf
commit 8288079985

View File

@ -1,173 +1,241 @@
// app/api/contact/route.js
// ❌ REMOVE "use client" from here - API routes must NOT have this directive
import { NextResponse } from 'next/server';
import nodemailer from 'nodemailer';
// Create transporter with Zoho configuration
const createTransport = () => {
return nodemailer.createTransport({
// Test environment variables function
function testEnvironmentVariables() {
console.log('\n🔍 === TESTING ENVIRONMENT VARIABLES ===');
const envVars = {
ZOHO_EMAIL: process.env.ZOHO_EMAIL,
ZOHO_PASSWORD: process.env.ZOHO_PASSWORD,
RECIPIENT_EMAIL: process.env.RECIPIENT_EMAIL,
};
console.log('Environment check:');
Object.entries(envVars).forEach(([key, value]) => {
if (value) {
console.log(`${key}: ${key === 'ZOHO_PASSWORD' ? value.slice(0, 4) + '*'.repeat(value.length - 4) : value}`);
} else {
console.log(`${key}: MISSING`);
}
});
const missing = Object.entries(envVars).filter(([key, value]) => !value);
if (missing.length > 0) {
console.log('❌ Missing variables:', missing.map(([key]) => key).join(', '));
return false;
}
console.log('✅ All environment variables are set');
return true;
}
// Test SMTP connection with multiple configurations
async function testSMTPConnection() {
console.log('\n🔍 === TESTING SMTP CONNECTION ===');
// Configuration 1: SSL with port 465 (Recommended for Zoho)
const config1 = {
host: 'smtp.zoho.com',
port: 465,
secure: true, // SSL
auth: {
user: process.env.ZOHO_EMAIL,
pass: process.env.ZOHO_PASSWORD,
},
debug: false,
logger: false,
};
// Configuration 2: STARTTLS with port 587
const config2 = {
host: 'smtp.zoho.com',
port: 587,
secure: false,
requireTLS: true,
auth: {
user: process.env.ZOHO_EMAIL,
pass: process.env.ZOHO_PASSWORD,
},
debug: false,
logger: false,
};
// Configuration 3: Alternative host for domain emails
const config3 = {
host: 'smtp.zoho.in',
port: 465,
secure: true,
auth: {
user: process.env.ZOHO_EMAIL,
pass: process.env.ZOHO_PASSWORD,
},
debug: false,
logger: false,
};
const configs = [
{ name: 'SSL Port 465 (smtp.zoho.com)', config: config1 },
{ name: 'STARTTLS Port 587 (smtp.zoho.com)', config: config2 },
{ name: 'SSL Port 465 (smtp.zoho.in)', config: config3 }
];
for (const { name, config } of configs) {
try {
console.log(`📡 Testing ${name}...`);
const transporter = nodemailer.createTransport(config);
await transporter.verify();
console.log(`✅ SMTP connection successful with ${name}`);
return { success: true, transporter, config };
} catch (error) {
console.log(`${name} failed: ${error.message}`);
}
}
return {
success: false,
error: new Error('All SMTP configurations failed. Please check your credentials and enable App Password.')
};
}
// Create transporter with the successful configuration
const createTransporter = (config = null) => {
// Use the config that worked during testing, or fall back to SSL default
const defaultConfig = {
host: 'smtp.zoho.com',
port: 465,
secure: true, // SSL
auth: {
user: process.env.ZOHO_EMAIL,
pass: process.env.ZOHO_PASSWORD,
},
debug: true,
logger: true,
});
};
// Handle Newsletter Subscription
async function handleNewsletter(data) {
console.log('Processing newsletter subscription for:', data.email);
const transporter = createTransport();
const mailOptions = {
from: process.env.ZOHO_EMAIL,
to: process.env.RECIPIENT_EMAIL,
subject: 'New Newsletter Subscription - Keystone Solutions',
html: `
<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
<h2 style="color: #ff3a2d;">New Newsletter Subscription</h2>
<div style="background: #f5f5f5; padding: 20px; border-radius: 8px;">
<p><strong>Email:</strong> ${data.email}</p>
<p><strong>Subscribed at:</strong> ${new Date().toLocaleString()}</p>
</div>
<hr style="margin: 20px 0;">
<p style="color: #666; font-size: 14px;">
This email was sent from your website's newsletter subscription form.
</p>
</div>
`,
};
const result = await transporter.sendMail(mailOptions);
console.log('Newsletter email sent successfully:', result.messageId);
return result;
}
return nodemailer.createTransport(config || defaultConfig);
};
// Handle Contact Form
async function handleContact(data) {
console.log('Processing contact form for:', data.fullName, data.email);
async function handleContact(data, smtpConfig = null) {
console.log('📝 Processing contact form...');
const transporter = createTransport();
const transporter = createTransporter(smtpConfig);
const mailOptions = {
from: process.env.ZOHO_EMAIL,
to: process.env.RECIPIENT_EMAIL,
subject: 'New Contact Form Submission - Keystone Solutions',
replyTo: data.email, // User can reply directly to the person who submitted the form
subject: `New Contact Form Submission from ${data.fullName} - Keystone Solutions`,
html: `
<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
<h2 style="color: #ff3a2d;">New Contact Form Submission</h2>
<div style="background: #f5f5f5; padding: 20px; border-radius: 8px;">
<p><strong>Full Name:</strong> ${data.fullName}</p>
<p><strong>Email:</strong> ${data.email}</p>
<p><strong>Email:</strong> <a href="mailto:${data.email}">${data.email}</a></p>
<p><strong>Phone:</strong> ${data.phone || 'Not provided'}</p>
<p><strong>Message:</strong></p>
<div style="background: white; padding: 15px; border-left: 4px solid #ff3a2d; margin: 10px 0;">
${data.message || 'No message provided'}
</div>
<p><strong>Submitted at:</strong> ${new Date().toLocaleString()}</p>
<hr style="margin: 20px 0; border: none; border-top: 1px solid #ddd;">
<p style="font-size: 12px; color: #666;">
💡 <strong>Tip:</strong> Click "Reply" to respond directly to ${data.fullName} at ${data.email}
</p>
</div>
<hr style="margin: 20px 0;">
<p style="color: #666; font-size: 14px;">
This email was sent from your website's contact form.
</p>
</div>
`,
};
console.log('📧 Attempting to send email...');
const result = await transporter.sendMail(mailOptions);
console.log('Contact email sent successfully:', result.messageId);
console.log('✅ Email sent successfully:', result.messageId);
return result;
}
// Handle Career Form
async function handleCareer(data) {
console.log('Processing career form for:', data.fullName, data.email);
async function handleCareer(data, smtpConfig = null) {
console.log('📝 Processing career form...');
const transporter = createTransport();
const attachments = [];
// Handle file attachment if present
if (data.resume) {
console.log('Resume file attached:', data.resume.name);
attachments.push({
filename: data.resume.name,
content: Buffer.from(data.resume.data, 'base64'),
contentType: data.resume.type,
});
}
const transporter = createTransporter(smtpConfig);
const mailOptions = {
from: process.env.ZOHO_EMAIL,
to: process.env.RECIPIENT_EMAIL,
subject: 'New Career Application - Keystone Solutions',
replyTo: data.email, // User can reply directly to the applicant
subject: `New Career Application from ${data.fullName} for ${data.roles} - Keystone Solutions`,
html: `
<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
<h2 style="color: #ff3a2d;">New Career Application</h2>
<div style="background: #f5f5f5; padding: 20px; border-radius: 8px;">
<p><strong>Full Name:</strong> ${data.fullName}</p>
<p><strong>Email:</strong> ${data.email}</p>
<p><strong>Phone:</strong> ${data.phone}</p>
<p><strong>Email:</strong> <a href="mailto:${data.email}">${data.email}</a></p>
<p><strong>Phone:</strong> <a href="tel:${data.phone}">${data.phone}</a></p>
<p><strong>Roles of Interest:</strong> ${data.roles}</p>
<p><strong>Resume:</strong> ${data.resume ? 'Attached' : 'Not provided'}</p>
<p><strong>Resume:</strong> ${data.resume ? `📎 ${data.resume.name} (attached)` : 'Not provided'}</p>
<p><strong>Applied at:</strong> ${new Date().toLocaleString()}</p>
<hr style="margin: 20px 0; border: none; border-top: 1px solid #ddd;">
<p style="font-size: 12px; color: #666;">
💡 <strong>Tip:</strong> Click "Reply" to respond directly to ${data.fullName} at ${data.email}
</p>
</div>
<hr style="margin: 20px 0;">
<p style="color: #666; font-size: 14px;">
This email was sent from your website's career application form.
</p>
</div>
`,
attachments: attachments,
attachments: data.resume ? [{
filename: data.resume.name,
content: data.resume.data,
encoding: 'base64'
}] : []
};
console.log('📧 Attempting to send career application email...');
const result = await transporter.sendMail(mailOptions);
console.log('Career email sent successfully:', result.messageId);
console.log('Career application email sent successfully:', result.messageId);
return result;
}
// POST handler for all form submissions
// POST handler with comprehensive debugging
export async function POST(request) {
console.log('\n🚀 === EMAIL API ROUTE STARTED ===');
console.log('\n🚀 === CONTACT API ROUTE STARTED ===');
try {
// Check environment variables first
const requiredEnvVars = {
ZOHO_EMAIL: process.env.ZOHO_EMAIL,
ZOHO_PASSWORD: process.env.ZOHO_PASSWORD,
RECIPIENT_EMAIL: process.env.RECIPIENT_EMAIL,
};
console.log('Environment variables check:');
Object.entries(requiredEnvVars).forEach(([key, value]) => {
console.log(`${key}: ${value ? '✅ Set' : '❌ Missing'}`);
});
console.log(
'Loaded password (first 3 chars):',
process.env.ZOHO_PASSWORD?.slice(0, 3)
);
if (!requiredEnvVars.ZOHO_EMAIL || !requiredEnvVars.ZOHO_PASSWORD || !requiredEnvVars.RECIPIENT_EMAIL) {
console.error('❌ Missing required environment variables');
// Step 1: Test environment variables
const envTest = testEnvironmentVariables();
if (!envTest) {
return NextResponse.json(
{ error: 'Email configuration missing' },
{
error: 'Environment variables missing',
details: 'Please check your .env.local file'
},
{ status: 500 }
);
}
// Step 2: Test SMTP connection
const smtpTest = await testSMTPConnection();
if (!smtpTest.success) {
return NextResponse.json(
{
error: 'SMTP connection failed',
details: smtpTest.error.message,
code: smtpTest.error.code
},
{ status: 500 }
);
}
// Parse request body
// Step 3: Parse request body
console.log('\n📝 === PARSING REQUEST ===');
let body;
try {
body = await request.json();
console.log('✅ Request body parsed successfully');
console.log('✅ Request parsed successfully');
console.log('Form type:', body.type);
console.log('Data keys:', Object.keys(body).join(', '));
} catch (parseError) {
console.error('❌ Failed to parse request body:', parseError);
console.error('❌ Failed to parse request:', parseError.message);
return NextResponse.json(
{ error: 'Invalid JSON in request body' },
{ status: 400 }
@ -175,92 +243,84 @@ export async function POST(request) {
}
const { type, ...data } = body;
console.log(`📝 Form type: ${type}`);
console.log(`📋 Form data keys: ${Object.keys(data).join(', ')}`);
// Step 4: Validate request
if (!type) {
console.error('❌ No form type specified');
return NextResponse.json(
{ error: 'Form type is required' },
{ status: 400 }
);
}
// Route to appropriate handler based on form type
let result;
switch (type) {
case 'newsletter':
if (!data.email) {
console.error('❌ Newsletter: Email is required');
return NextResponse.json({ error: 'Email is required' }, { status: 400 });
}
result = await handleNewsletter(data);
break;
case 'contact':
if (!data.fullName || !data.email) {
console.error('❌ Contact: Full name and email are required');
return NextResponse.json({ error: 'Full name and email are required' }, { status: 400 });
}
result = await handleContact(data);
break;
case 'career':
if (!data.fullName || !data.email || !data.phone) {
console.error('❌ Career: Full name, email, and phone are required');
return NextResponse.json({ error: 'Full name, email, and phone are required' }, { status: 400 });
}
result = await handleCareer(data);
break;
default:
console.error('❌ Invalid form type:', type);
return NextResponse.json({ error: 'Invalid form type' }, { status: 400 });
if (!['contact', 'career'].includes(type)) {
return NextResponse.json(
{ error: 'Invalid form type' },
{ status: 400 }
);
}
console.log('✅ Email sent successfully!');
console.log('📧 Message ID:', result.messageId);
console.log('🎉 === EMAIL API ROUTE SUCCESS ===\n');
// Common validation
if (!data.fullName || !data.email) {
return NextResponse.json(
{ error: 'Full name and email are required' },
{ status: 400 }
);
}
// Career form specific validation
if (type === 'career') {
if (!data.phone || !data.roles) {
return NextResponse.json(
{ error: 'Phone and roles are required for career applications' },
{ status: 400 }
);
}
}
// Step 5: Send email based on form type
console.log('\n📧 === SENDING EMAIL ===');
let result;
if (type === 'contact') {
result = await handleContact(data, smtpTest.config);
} else if (type === 'career') {
result = await handleCareer(data, smtpTest.config);
}
console.log('🎉 === SUCCESS ===');
return NextResponse.json(
{
message: 'Email sent successfully',
messageId: result.messageId,
type: type
type: type
},
{ status: 200 }
);
} catch (error) {
console.error('\n💥 === EMAIL API ROUTE ERROR ===');
console.error('\n💥 === ERROR OCCURRED ===');
console.error('Error name:', error.name);
console.error('Error message:', error.message);
console.error('Error code:', error.code);
console.error('Error stack:', error.stack);
console.error('Full error:', error);
// Provide more specific error messages
// Determine specific error response
let errorMessage = 'Failed to send email';
let statusCode = 500;
if (error.code === 'EAUTH') {
errorMessage = 'Authentication failed. Please check your Zoho email and app password.';
console.error('🔐 Authentication issue - check ZOHO_EMAIL and ZOHO_PASSWORD');
errorMessage = 'Email authentication failed. Check your Zoho credentials.';
} else if (error.code === 'ECONNECTION' || error.code === 'ETIMEDOUT') {
errorMessage = 'Connection to email server failed. Please try again.';
console.error('🌐 Network connection issue');
} else if (error.code === 'EMESSAGE') {
errorMessage = 'Invalid email message format.';
statusCode = 400;
console.error('📧 Email format issue');
errorMessage = 'Connection to email server failed.';
} else if (error.message?.includes('Invalid login')) {
errorMessage = 'Invalid email or password. Please check your Zoho app password.';
}
console.error('💥 === END ERROR DETAILS ===\n');
return NextResponse.json(
{
error: errorMessage,
details: error.message,
code: error.code
code: error.code || 'UNKNOWN'
},
{ status: statusCode }
);