feat: replace Resend SDK with Mailcow SMTP (nodemailer)
Swap email sending from Resend API to self-hosted Mailcow at mx.jeffemmett.com. Uses SMTP_PASS env var instead of RESEND_API_KEY. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
be4e3788be
commit
620bc229ed
|
|
@ -1,8 +1,8 @@
|
||||||
// Application form API endpoint
|
// Application form API endpoint
|
||||||
// Handles full event applications with PostgreSQL storage and Resend emails
|
// Handles full event applications with PostgreSQL storage and Mailcow SMTP emails
|
||||||
|
|
||||||
const { Pool } = require('pg');
|
const { Pool } = require('pg');
|
||||||
const { Resend } = require('resend');
|
const nodemailer = require('nodemailer');
|
||||||
|
|
||||||
// Initialize PostgreSQL connection pool
|
// Initialize PostgreSQL connection pool
|
||||||
const pool = new Pool({
|
const pool = new Pool({
|
||||||
|
|
@ -10,8 +10,16 @@ const pool = new Pool({
|
||||||
ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false
|
ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false
|
||||||
});
|
});
|
||||||
|
|
||||||
// Initialize Resend
|
// Initialize SMTP transport (Mailcow)
|
||||||
const resend = new Resend(process.env.RESEND_API_KEY);
|
const smtp = nodemailer.createTransport({
|
||||||
|
host: process.env.SMTP_HOST || 'mx.jeffemmett.com',
|
||||||
|
port: parseInt(process.env.SMTP_PORT || '587'),
|
||||||
|
secure: false,
|
||||||
|
auth: {
|
||||||
|
user: process.env.SMTP_USER || 'noreply@jeffemmett.com',
|
||||||
|
pass: process.env.SMTP_PASS || '',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
// Email templates
|
// Email templates
|
||||||
const confirmationEmail = (application) => ({
|
const confirmationEmail = (application) => ({
|
||||||
|
|
@ -238,17 +246,17 @@ module.exports = async function handler(req, res) {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Send confirmation email to applicant
|
// Send confirmation email to applicant
|
||||||
if (process.env.RESEND_API_KEY) {
|
if (process.env.SMTP_PASS) {
|
||||||
try {
|
try {
|
||||||
const confirmEmail = confirmationEmail(application);
|
const confirmEmail = confirmationEmail(application);
|
||||||
const { data: emailData } = await resend.emails.send({
|
const info = await smtp.sendMail({
|
||||||
from: process.env.EMAIL_FROM || 'Valley of the Commons <noreply@jeffemmett.com>',
|
from: process.env.EMAIL_FROM || 'Valley of the Commons <noreply@jeffemmett.com>',
|
||||||
to: application.email,
|
to: application.email,
|
||||||
subject: confirmEmail.subject,
|
subject: confirmEmail.subject,
|
||||||
html: confirmEmail.html
|
html: confirmEmail.html,
|
||||||
});
|
});
|
||||||
await logEmail(application.email, `${application.first_name} ${application.last_name}`,
|
await logEmail(application.email, `${application.first_name} ${application.last_name}`,
|
||||||
'application_confirmation', confirmEmail.subject, emailData?.id, { applicationId: application.id });
|
'application_confirmation', confirmEmail.subject, info.messageId, { applicationId: application.id });
|
||||||
} catch (emailError) {
|
} catch (emailError) {
|
||||||
console.error('Failed to send confirmation email:', emailError);
|
console.error('Failed to send confirmation email:', emailError);
|
||||||
}
|
}
|
||||||
|
|
@ -257,14 +265,14 @@ module.exports = async function handler(req, res) {
|
||||||
try {
|
try {
|
||||||
const adminEmail = adminNotificationEmail(application);
|
const adminEmail = adminNotificationEmail(application);
|
||||||
const adminRecipients = (process.env.ADMIN_EMAILS || 'jeff@jeffemmett.com').split(',');
|
const adminRecipients = (process.env.ADMIN_EMAILS || 'jeff@jeffemmett.com').split(',');
|
||||||
const { data: emailData } = await resend.emails.send({
|
const info = await smtp.sendMail({
|
||||||
from: process.env.EMAIL_FROM || 'Valley of the Commons <noreply@jeffemmett.com>',
|
from: process.env.EMAIL_FROM || 'Valley of the Commons <noreply@jeffemmett.com>',
|
||||||
to: adminRecipients,
|
to: adminRecipients.join(', '),
|
||||||
subject: adminEmail.subject,
|
subject: adminEmail.subject,
|
||||||
html: adminEmail.html
|
html: adminEmail.html,
|
||||||
});
|
});
|
||||||
await logEmail(adminRecipients[0], 'Admin', 'admin_notification',
|
await logEmail(adminRecipients[0], 'Admin', 'admin_notification',
|
||||||
adminEmail.subject, emailData?.id, { applicationId: application.id });
|
adminEmail.subject, info.messageId, { applicationId: application.id });
|
||||||
} catch (emailError) {
|
} catch (emailError) {
|
||||||
console.error('Failed to send admin notification:', emailError);
|
console.error('Failed to send admin notification:', emailError);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
// Waitlist API endpoint using PostgreSQL
|
// Waitlist API endpoint using PostgreSQL
|
||||||
// Simple interest signups with email confirmation via Resend
|
// Simple interest signups with email confirmation via Mailcow SMTP
|
||||||
|
|
||||||
const { Pool } = require('pg');
|
const { Pool } = require('pg');
|
||||||
const { Resend } = require('resend');
|
const nodemailer = require('nodemailer');
|
||||||
|
|
||||||
// Initialize PostgreSQL connection pool
|
// Initialize PostgreSQL connection pool
|
||||||
const pool = new Pool({
|
const pool = new Pool({
|
||||||
|
|
@ -10,8 +10,16 @@ const pool = new Pool({
|
||||||
ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false
|
ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false
|
||||||
});
|
});
|
||||||
|
|
||||||
// Initialize Resend
|
// Initialize SMTP transport (Mailcow)
|
||||||
const resend = new Resend(process.env.RESEND_API_KEY);
|
const smtp = nodemailer.createTransport({
|
||||||
|
host: process.env.SMTP_HOST || 'mx.jeffemmett.com',
|
||||||
|
port: parseInt(process.env.SMTP_PORT || '587'),
|
||||||
|
secure: false,
|
||||||
|
auth: {
|
||||||
|
user: process.env.SMTP_USER || 'noreply@jeffemmett.com',
|
||||||
|
pass: process.env.SMTP_PASS || '',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const welcomeEmail = (signup) => ({
|
const welcomeEmail = (signup) => ({
|
||||||
subject: 'Welcome to Valley of the Commons',
|
subject: 'Welcome to Valley of the Commons',
|
||||||
|
|
@ -137,16 +145,16 @@ module.exports = async function handler(req, res) {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Send welcome email
|
// Send welcome email
|
||||||
if (process.env.RESEND_API_KEY) {
|
if (process.env.SMTP_PASS) {
|
||||||
try {
|
try {
|
||||||
const email = welcomeEmail(signup);
|
const email = welcomeEmail(signup);
|
||||||
const { data: emailData } = await resend.emails.send({
|
const info = await smtp.sendMail({
|
||||||
from: process.env.EMAIL_FROM || 'Valley of the Commons <noreply@jeffemmett.com>',
|
from: process.env.EMAIL_FROM || 'Valley of the Commons <noreply@jeffemmett.com>',
|
||||||
to: signup.email,
|
to: signup.email,
|
||||||
subject: email.subject,
|
subject: email.subject,
|
||||||
html: email.html
|
html: email.html,
|
||||||
});
|
});
|
||||||
await logEmail(signup.email, signup.name, 'waitlist_welcome', email.subject, emailData?.id);
|
await logEmail(signup.email, signup.name, 'waitlist_welcome', email.subject, info.messageId);
|
||||||
} catch (emailError) {
|
} catch (emailError) {
|
||||||
console.error('Failed to send welcome email:', emailError);
|
console.error('Failed to send welcome email:', emailError);
|
||||||
// Don't fail the request if email fails
|
// Don't fail the request if email fails
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,8 @@
|
||||||
"@octokit/rest": "^22.0.1",
|
"@octokit/rest": "^22.0.1",
|
||||||
"ai": "^6.0.1",
|
"ai": "^6.0.1",
|
||||||
"googleapis": "^126.0.1",
|
"googleapis": "^126.0.1",
|
||||||
"pg": "^8.13.0",
|
"nodemailer": "^6.9.0",
|
||||||
"resend": "^4.0.0"
|
"pg": "^8.13.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"dotenv": "^16.3.1"
|
"dotenv": "^16.3.1"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue