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 " const INTERNAL_NOTIFY_EMAIL = "contact@cryptocommonsgather.ing" interface BookingNotificationData { guestName: string guestEmail: string accommodationType: string amountPaid: string bookingSuccess: boolean venue?: string room?: string bedType?: string error?: string } export async function sendBookingNotification( data: BookingNotificationData ): Promise { const transport = getTransporter() if (!transport) { console.log("[Email] SMTP not configured, skipping booking notification") return false } const statusColor = data.bookingSuccess ? "#16a34a" : "#dc2626" const statusLabel = data.bookingSuccess ? "ASSIGNED" : "FAILED" const flags: string[] = [] if (!data.bookingSuccess) { flags.push(`Booking assignment failed: ${data.error || "unknown reason"}`) } if (!data.guestEmail) { flags.push("No email address on file for this guest") } const html = `

Accommodation Update: ${data.guestName}

${statusLabel}

${data.bookingSuccess ? ` ` : ""}
Guest:${data.guestName}
Email:${data.guestEmail || "N/A"}
Paid:${data.amountPaid}
Requested:${data.accommodationType}
Assigned Venue:${data.venue}
Room:${data.room}
Bed Type:${data.bedType}
${ flags.length > 0 ? `
Flags:
    ${flags.map((f) => `
  • ${f}
  • `).join("")}
` : `

No issues detected. Booking sheet updated automatically.

` }

Automated notification from CCG registration system

` try { const info = await transport.sendMail({ from: EMAIL_FROM, to: INTERNAL_NOTIFY_EMAIL, subject: `[CCG Booking] ${statusLabel}: ${data.guestName} — ${data.accommodationType}`, html, }) console.log(`[Email] Booking notification sent to ${INTERNAL_NOTIFY_EMAIL} (${info.messageId})`) return true } catch (error) { console.error("[Email] Failed to send booking notification:", error) return false } } interface PaymentConfirmationData { name: string email: string amountPaid: string paymentMethod: string contributions: string dietary: string accommodationVenue?: string accommodationRoom?: string } export async function sendPaymentConfirmation( data: PaymentConfirmationData ): Promise { const transport = getTransporter() if (!transport) { console.log("[Email] SMTP not configured, skipping confirmation email") return false } const html = `

You're In!

Crypto Commons Gathering 2026

Dear ${data.name},

Your payment of ${data.amountPaid} has been confirmed. You are now registered for Crypto Commons Gathering 2026 in Austria's Höllental Valley, August 16–23, 2026.

Registration Details

${data.dietary ? `` : ""} ${data.accommodationVenue ? `` : ""}
Amount: ${data.amountPaid}
Payment: ${data.paymentMethod}
Dietary:${data.dietary}
Accommodation:${data.accommodationVenue}${data.accommodationRoom ? `, Room ${data.accommodationRoom}` : ""}

Food & Accommodation

${ data.accommodationVenue ? `

Your accommodation at the ${data.accommodationVenue}${data.accommodationRoom ? ` (Room ${data.accommodationRoom})` : ""} has been reserved for the duration of the gathering.

We'll be in touch about food arrangements as we work to keep costs down while creating inclusive, participatory processes for the event.

` : `

We'll be in touch about food arrangements as we work to keep costs down while creating inclusive, participatory processes for the event. If you haven't yet decided on accommodation, the Commons Hub (our main venue) and the nearby Herrnhof Villa are the most convenient options — right in the heart of the gathering. Email contact@cryptocommonsgather.ing for any questions.

` }

What's Next?

  • Join the CCG26 Telegram group to connect with other participants
  • Start preparing your session proposals
  • 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

See you in the valley,
The Crypto Commons Gathering Team


You received this email because you registered at cryptocommonsgather.ing.
cryptocommonsgather.ing

` try { const info = await transport.sendMail({ from: EMAIL_FROM, to: data.email, 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 } }