feat: implement email sending functionality with SMTP and Resend fallback
CI / skip-ci-check (pull_request) Successful in 29s
CI / lint-and-type-check (pull_request) Has been cancelled
CI / python-lint (pull_request) Has been cancelled
CI / test-backend (pull_request) Has been cancelled
CI / build (pull_request) Has been cancelled
CI / secret-scanning (pull_request) Has been cancelled
CI / dependency-scan (pull_request) Has been cancelled
CI / sast-scan (pull_request) Has been cancelled
CI / workflow-summary (pull_request) Has been cancelled

- Added support for sending emails using SMTP configuration or Resend API as a fallback.
- Updated environment variables in .env_example for SMTP settings.
- Enhanced email confirmation process with improved error handling and fallback logic.
- Introduced a test script to validate email sending configuration and functionality.
This commit is contained in:
Tanya
2026-03-23 15:23:55 -04:00
parent 064daf47f7
commit a5838a8373
6 changed files with 362 additions and 188 deletions
+74 -40
View File
@@ -1,4 +1,5 @@
import { Resend } from 'resend';
import nodemailer from 'nodemailer';
import * as dotenv from 'dotenv';
// Load environment variables
@@ -6,63 +7,96 @@ dotenv.config();
const resend = new Resend(process.env.RESEND_API_KEY);
function smtpEnabled(): boolean {
return Boolean(
process.env.SMTP_HOST &&
process.env.SMTP_USER &&
process.env.SMTP_PASS
);
}
async function testEmailSending() {
console.log('🧪 Testing email sending configuration...\n');
// Check environment variables
console.log('📋 Environment Variables:');
console.log(' EMAIL_PROVIDER:', process.env.EMAIL_PROVIDER || 'smtp (default)');
console.log(' SMTP_HOST:', process.env.SMTP_HOST || '❌ NOT SET');
console.log(' SMTP_PORT:', process.env.SMTP_PORT || '465 (default)');
console.log(' SMTP_SECURE:', process.env.SMTP_SECURE || 'true (default)');
console.log(' SMTP_USER:', process.env.SMTP_USER || '❌ NOT SET');
console.log(' SMTP_FROM_EMAIL:', process.env.SMTP_FROM_EMAIL || '❌ NOT SET');
console.log(' RESEND_API_KEY:', process.env.RESEND_API_KEY ? `${process.env.RESEND_API_KEY.substring(0, 10)}...` : '❌ NOT SET');
console.log(' RESEND_FROM_EMAIL:', process.env.RESEND_FROM_EMAIL || '❌ NOT SET');
console.log(' NEXTAUTH_URL:', process.env.NEXTAUTH_URL || '❌ NOT SET');
console.log('');
if (!process.env.RESEND_API_KEY) {
console.error('❌ RESEND_API_KEY is not set in .env file');
const toEmail = process.env.TEST_EMAIL_TO || process.env.SMTP_USER || process.env.RESEND_FROM_EMAIL;
if (!toEmail) {
console.error('❌ Set TEST_EMAIL_TO or SMTP_USER in .env to run this test.');
process.exit(1);
}
if (!process.env.RESEND_FROM_EMAIL) {
console.error('❌ RESEND_FROM_EMAIL is not set in .env file');
if (smtpEnabled()) {
console.log('📤 Attempting SMTP test first...');
try {
const transporter = nodemailer.createTransport({
host: process.env.SMTP_HOST,
port: Number(process.env.SMTP_PORT || 465),
secure: (process.env.SMTP_SECURE || 'true').toLowerCase() === 'true',
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS,
},
});
const smtpFromEmail = process.env.SMTP_FROM_EMAIL || process.env.SMTP_USER!;
const smtpFromName = process.env.SMTP_FROM_NAME;
const smtpReplyTo = process.env.SMTP_REPLY_TO || smtpFromEmail;
const smtpFrom = smtpFromName
? `${smtpFromName} <${smtpFromEmail}>`
: smtpFromEmail;
const info = await transporter.sendMail({
from: smtpFrom,
to: toEmail,
replyTo: smtpReplyTo,
subject: 'SMTP test from PunimTag',
text: 'SMTP test email from PunimTag viewer frontend.',
});
console.log('✅ SMTP test sent successfully');
console.log(' Message ID:', info.messageId);
process.exit(0);
} catch (smtpError: any) {
console.error('⚠️ SMTP test failed, will try Resend fallback.');
console.error(' SMTP error:', smtpError.message);
}
} else {
console.log('️ SMTP config not set, skipping SMTP test.');
}
if (!process.env.RESEND_API_KEY || !process.env.RESEND_FROM_EMAIL) {
console.error('❌ Resend fallback not configured (RESEND_API_KEY/RESEND_FROM_EMAIL missing).');
process.exit(1);
}
// Clean up the from email (remove quotes and spaces)
const fromEmail = process.env.RESEND_FROM_EMAIL.trim().replace(/^["']|["']$/g, '');
console.log('📧 Using FROM email:', fromEmail);
console.log('');
// Test email sending
console.log('📤 Attempting to send test email...');
const resendFrom = process.env.RESEND_FROM_EMAIL.trim().replace(/^["']|["']$/g, '');
console.log('📤 Attempting Resend fallback test...');
try {
const result = await resend.emails.send({
from: fromEmail,
to: 'test@example.com', // This will fail but we'll see the error
subject: 'Test Email',
html: '<p>This is a test email</p>',
from: resendFrom,
to: toEmail,
subject: 'Resend fallback test from PunimTag',
html: '<p>Resend fallback test email from PunimTag viewer frontend.</p>',
});
console.log('✅ Email API call successful!');
console.log('Response:', JSON.stringify(result, null, 2));
if (result.error) {
throw new Error(result.error.message || 'Unknown Resend API error');
}
console.log('✅ Resend fallback test sent successfully');
console.log(' Email ID:', result.data?.id);
} catch (error: any) {
console.error('❌ Error sending email:');
console.error(' Message:', error.message);
if (error.response) {
console.error(' Response:', JSON.stringify(error.response, null, 2));
}
// Check for common errors
if (error.message?.includes('domain')) {
console.error('\n⚠️ Domain verification issue:');
console.error(' The email domain needs to be verified in Resend dashboard');
console.error(' For testing, use: onboarding@resend.dev');
}
if (error.message?.includes('unauthorized') || error.message?.includes('Invalid API key')) {
console.error('\n⚠️ API Key issue:');
console.error(' Check that your RESEND_API_KEY is correct');
console.error(' Get a new key from: https://resend.com/api-keys');
}
console.error('❌ Resend fallback failed:', error.message);
process.exit(1);
}
}