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
c32070806a
commit
c3e67b9cae
|
|
@ -19,8 +19,8 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"googleapis": "^144.0.0",
|
"googleapis": "^144.0.0",
|
||||||
"pg": "^8.11.3",
|
"nodemailer": "^6.9.0",
|
||||||
"resend": "^4.0.0"
|
"pg": "^8.11.3"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.0.0"
|
"node": ">=18.0.0"
|
||||||
|
|
|
||||||
24
server.js
24
server.js
|
|
@ -2,7 +2,7 @@ const express = require('express');
|
||||||
const fs = require('fs').promises;
|
const fs = require('fs').promises;
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const { google } = require('googleapis');
|
const { google } = require('googleapis');
|
||||||
const { Resend } = require('resend');
|
const nodemailer = require('nodemailer');
|
||||||
const { Pool } = require('pg');
|
const { Pool } = require('pg');
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
@ -10,8 +10,16 @@ const PORT = process.env.PORT || 3000;
|
||||||
const DATA_DIR = process.env.DATA_DIR || './data';
|
const DATA_DIR = process.env.DATA_DIR || './data';
|
||||||
const REGISTRATIONS_FILE = path.join(DATA_DIR, 'registrations.json');
|
const REGISTRATIONS_FILE = path.join(DATA_DIR, 'registrations.json');
|
||||||
|
|
||||||
// Initialize Resend for emails
|
// Initialize SMTP transport (Mailcow)
|
||||||
const resend = process.env.RESEND_API_KEY ? new Resend(process.env.RESEND_API_KEY) : null;
|
const smtp = process.env.SMTP_PASS ? 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,
|
||||||
|
},
|
||||||
|
}) : null;
|
||||||
|
|
||||||
// Listmonk PostgreSQL configuration for newsletter management
|
// Listmonk PostgreSQL configuration for newsletter management
|
||||||
const LISTMONK_LIST_ID = parseInt(process.env.LISTMONK_LIST_ID) || 20; // WORLDPLAY list
|
const LISTMONK_LIST_ID = parseInt(process.env.LISTMONK_LIST_ID) || 20; // WORLDPLAY list
|
||||||
|
|
@ -109,8 +117,8 @@ async function appendToGoogleSheet(registration) {
|
||||||
|
|
||||||
// Send confirmation email
|
// Send confirmation email
|
||||||
async function sendConfirmationEmail(registration) {
|
async function sendConfirmationEmail(registration) {
|
||||||
if (!resend) {
|
if (!smtp) {
|
||||||
console.log('Resend not configured, skipping email...');
|
console.log('SMTP not configured, skipping email...');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -156,8 +164,8 @@ async function sendConfirmationEmail(registration) {
|
||||||
? registration.contribute.map(c => contributeLabels[c] || c).join(', ')
|
? registration.contribute.map(c => contributeLabels[c] || c).join(', ')
|
||||||
: 'Not specified';
|
: 'Not specified';
|
||||||
|
|
||||||
await resend.emails.send({
|
await smtp.sendMail({
|
||||||
from: 'WORLDPLAY <hello@worldplay.art>',
|
from: process.env.EMAIL_FROM || 'WORLDPLAY <noreply@jeffemmett.com>',
|
||||||
to: registration.email,
|
to: registration.email,
|
||||||
subject: '🎭 Welcome to WORLDPLAY – Registration Confirmed',
|
subject: '🎭 Welcome to WORLDPLAY – Registration Confirmed',
|
||||||
html: `
|
html: `
|
||||||
|
|
@ -513,7 +521,7 @@ ensureDataDir().then(() => {
|
||||||
console.log(`WORLDPLAY server running on port ${PORT}`);
|
console.log(`WORLDPLAY server running on port ${PORT}`);
|
||||||
console.log(`Admin token: ${process.env.ADMIN_TOKEN || 'worldplay-admin-2026'}`);
|
console.log(`Admin token: ${process.env.ADMIN_TOKEN || 'worldplay-admin-2026'}`);
|
||||||
console.log(`Google Sheets: ${sheets ? 'enabled' : 'disabled'}`);
|
console.log(`Google Sheets: ${sheets ? 'enabled' : 'disabled'}`);
|
||||||
console.log(`Email notifications: ${resend ? 'enabled' : 'disabled'}`);
|
console.log(`Email notifications: ${smtp ? 'enabled (Mailcow SMTP)' : 'disabled (no SMTP_PASS)'}`);
|
||||||
console.log(`Listmonk newsletter sync: ${listmonkPool ? 'enabled' : 'disabled'} (list ID: ${LISTMONK_LIST_ID})`);
|
console.log(`Listmonk newsletter sync: ${listmonkPool ? 'enabled' : 'disabled'} (list ID: ${LISTMONK_LIST_ID})`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue