108 lines
4.5 KiB
TypeScript
108 lines
4.5 KiB
TypeScript
import nodemailer from "nodemailer"
|
|
|
|
// Lazy-initialized SMTP transport (Mailcow)
|
|
let transporter: nodemailer.Transporter | null = null
|
|
|
|
function getTransporter() {
|
|
if (!transporter && process.env.SMTP_PASS) {
|
|
transporter = nodemailer.createTransport({
|
|
host: process.env.SMTP_HOST || "mail.rmail.online",
|
|
port: parseInt(process.env.SMTP_PORT || "587"),
|
|
secure: false,
|
|
auth: {
|
|
user: process.env.SMTP_USER || "newsletter@cryptocommonsgather.ing",
|
|
pass: process.env.SMTP_PASS,
|
|
},
|
|
tls: { rejectUnauthorized: false },
|
|
})
|
|
}
|
|
return transporter
|
|
}
|
|
|
|
const EMAIL_FROM =
|
|
process.env.EMAIL_FROM ||
|
|
"Crypto Commons Gathering <newsletter@cryptocommonsgather.ing>"
|
|
|
|
interface PaymentConfirmationData {
|
|
name: string
|
|
email: string
|
|
amountPaid: string
|
|
paymentMethod: string
|
|
contributions: string
|
|
dietary: string
|
|
}
|
|
|
|
export async function sendPaymentConfirmation(
|
|
data: PaymentConfirmationData
|
|
): Promise<boolean> {
|
|
const transport = getTransporter()
|
|
if (!transport) {
|
|
console.log("[Email] SMTP not configured, skipping confirmation email")
|
|
return false
|
|
}
|
|
|
|
const html = `
|
|
<div style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; color: #333;">
|
|
<h1 style="color: #f59e0b; margin-bottom: 8px;">You're In!</h1>
|
|
<p style="font-size: 15px; color: #92400e; margin-top: 0; margin-bottom: 28px; font-style: italic;">Crypto Commons Gathering 2026</p>
|
|
|
|
<p>Dear ${data.name},</p>
|
|
|
|
<p>Your payment of <strong>${data.amountPaid}</strong> has been confirmed. You are now registered for <strong>Crypto Commons Gathering 2026</strong> in Austria's Höllental Valley, August 16–23, 2026.</p>
|
|
|
|
<div style="background: #fffbeb; padding: 20px; border-radius: 8px; margin: 24px 0; border-left: 3px solid #f59e0b;">
|
|
<h3 style="margin-top: 0; color: #92400e;">Registration Details</h3>
|
|
<table style="width: 100%; border-collapse: collapse;">
|
|
<tr>
|
|
<td style="padding: 4px 0;"><strong>Amount:</strong></td>
|
|
<td style="padding: 4px 0;">${data.amountPaid}</td>
|
|
</tr>
|
|
<tr>
|
|
<td style="padding: 4px 0;"><strong>Payment:</strong></td>
|
|
<td style="padding: 4px 0;">${data.paymentMethod}</td>
|
|
</tr>
|
|
${data.dietary ? `<tr><td style="padding: 4px 0;"><strong>Dietary:</strong></td><td style="padding: 4px 0;">${data.dietary}</td></tr>` : ""}
|
|
</table>
|
|
</div>
|
|
|
|
<h3 style="color: #92400e;">Food & Accommodation</h3>
|
|
<p>We'll follow up separately via email with food options and pricing. If you haven't yet decided on accommodation, there's still time! The <strong>Commons Hub</strong> (our main venue with 30 on-site beds) and the nearby <strong><a href="https://herrnhof.at" style="color: #f59e0b;">Herrnhof Villa</a></strong> are the most convenient options — right in the heart of the gathering. Other nearby options are also available; check the <a href="https://cryptocommonsgather.ing/directions" style="color: #f59e0b;">Directions page</a> for details.</p>
|
|
|
|
<h3 style="color: #92400e;">What's Next?</h3>
|
|
<ul style="line-height: 1.8;">
|
|
<li>Join the <a href="https://t.me/+n5V_wDVKWrk1ZTBh" style="color: #f59e0b;">CCG26 Telegram group</a> to connect with other participants</li>
|
|
<li>Start preparing your session proposals</li>
|
|
<li>We'll follow up with you via email in the coming weeks with further details on logistics, schedule, and how to make the most of your time in the valley</li>
|
|
</ul>
|
|
|
|
<p style="margin-top: 32px;">
|
|
See you in the valley,<br>
|
|
<strong>The Crypto Commons Gathering Team</strong>
|
|
</p>
|
|
|
|
<hr style="border: none; border-top: 1px solid #ddd; margin: 32px 0;">
|
|
<p style="font-size: 12px; color: #666;">
|
|
You received this email because you registered at cryptocommonsgather.ing.<br>
|
|
<a href="https://cryptocommonsgather.ing">cryptocommonsgather.ing</a>
|
|
</p>
|
|
</div>
|
|
`
|
|
|
|
try {
|
|
const info = await transport.sendMail({
|
|
from: EMAIL_FROM,
|
|
to: data.email,
|
|
bcc: "contact@cryptocommonsgather.ing",
|
|
subject: "Registration Confirmed - Crypto Commons Gathering 2026",
|
|
html,
|
|
})
|
|
console.log(
|
|
`[Email] Payment confirmation sent to ${data.email} (${info.messageId})`
|
|
)
|
|
return true
|
|
} catch (error) {
|
|
console.error("[Email] Failed to send payment confirmation:", error)
|
|
return false
|
|
}
|
|
}
|