import nodemailer from "nodemailer"
import { query } from "@/lib/db"
const ADMIN_EMAIL = process.env.ADMIN_EMAIL || ""
const SMTP_HOST = process.env.SMTP_HOST || ""
const SMTP_PORT = parseInt(process.env.SMTP_PORT || "587", 10)
const SMTP_USER = process.env.SMTP_USER || ""
const SMTP_PASS = process.env.SMTP_PASS || ""
const EMAIL_FROM = process.env.EMAIL_FROM || "noreply@coastit.co.za"
function createTransporter() {
const hasAuth = !!(SMTP_USER && SMTP_PASS)
const isLocal = SMTP_HOST === "127.0.0.1" || SMTP_HOST === "localhost"
const tlsOptions = hasAuth && !isLocal
? { rejectUnauthorized: true }
: { rejectUnauthorized: false }
return nodemailer.createTransport({
host: SMTP_HOST,
port: SMTP_PORT,
secure: SMTP_PORT === 465,
requireTLS: hasAuth && !isLocal,
...(hasAuth ? { auth: { user: SMTP_USER, pass: SMTP_PASS } } : {}),
tls: tlsOptions,
connectionTimeout: 5000,
greetingTimeout: 5000,
})
}
function formatDate(iso: string) {
return new Date(iso).toLocaleDateString("en-US", {
weekday: "long", month: "long", day: "numeric", year: "numeric",
})
}
function formatTime(iso: string) {
return new Date(iso).toLocaleTimeString("en-US", {
hour: "numeric", minute: "2-digit", hour12: true,
})
}
function formatDuration(min: number) {
if (min < 60) return `${min} minutes`
const h = Math.floor(min / 60)
const m = min % 60
return m > 0 ? `${h}h ${m}m` : `${h} hour${h > 1 ? "s" : ""}`
}
export interface EventConfirmationInput {
creatorName: string
creatorEmail: string
participantName: string | null
participantEmail: string | null
title: string
description?: string
eventType: string
startTime: string
endTime?: string
durationMinutes?: number
}
function buildEmail(event: EventConfirmationInput, variant: "scheduled" | "rescheduled" = "scheduled") {
const dateStr = formatDate(event.startTime)
const timeStr = formatTime(event.startTime)
const durStr = event.durationMinutes ? formatDuration(event.durationMinutes) : ""
const typeLabel = event.eventType.charAt(0).toUpperCase() + event.eventType.slice(1)
const heading = variant === "rescheduled" ? "Event Rescheduled" : "Event Scheduled"
const badge = variant === "rescheduled"
? '
This event has been rescheduled
'
: ""
const html = `
${heading}
${badge}
${event.title}
| Type | ${typeLabel} |
| Date | ${dateStr} |
| Time | ${timeStr} |
${durStr ? `| Duration | ${durStr} |
` : ""}
${event.description ? `| Notes | ${event.description} |
` : ""}
| Created by | ${event.creatorName} |
${event.participantName ? `| With | ${event.participantName} |
` : ""}
Automated confirmation from CRM.
`
const text = `${variant === "rescheduled" ? "[RESCHEDULED] " : ""}Event: ${event.title}
Type: ${typeLabel}
Date: ${dateStr}
Time: ${timeStr}
${durStr ? `Duration: ${durStr}` : ""}
${event.description ? `Notes: ${event.description}` : ""}
Created by: ${event.creatorName}
${event.participantName ? `With: ${event.participantName}` : ""}`
const prefix = variant === "rescheduled" ? "🔄 Rescheduled: " : ""
const subject = `${prefix}${event.title} — ${dateStr}`
return { html, text, subject, dateStr }
}
export async function sendEventConfirmation(event: EventConfirmationInput) {
const { html, text, subject } = buildEmail(event)
const recipients: string[] = []
if (event.creatorEmail) recipients.push(event.creatorEmail)
if (event.participantEmail && !recipients.includes(event.participantEmail)) recipients.push(event.participantEmail)
if (ADMIN_EMAIL && !recipients.includes(ADMIN_EMAIL)) recipients.push(ADMIN_EMAIL)
// 1. Always store in database
for (const to of recipients) {
await query(
`INSERT INTO sent_emails (recipient, subject, body_html, body_text)
VALUES ($1, $2, $3, $4)`,
[to, subject, html, text],
).catch(() => {})
}
if (recipients.length === 0) return
// 2. Send via SMTP if configured
if (SMTP_HOST) {
const transporter = createTransporter()
await transporter.sendMail({
from: EMAIL_FROM,
to: recipients.join(", "),
subject,
text,
html,
}).catch(() => {})
return
}
// 3. Fallback: log to console with instructions
console.log("=".repeat(60))
console.log(`📧 Email ready — ${subject}`)
console.log(` To: ${recipients.join(", ")}`)
console.log(` Body:\n${text}`)
console.log("=".repeat(60))
console.log("💡 To send real emails:")
console.log(" Fill in SMTP_HOST in .env (maildev works at localhost:1025 with no auth)")
console.log(" Or set SMTP_HOST/SMTP_USER/SMTP_PASS for authenticated relays")
}
export async function sendEventRescheduled(event: EventConfirmationInput) {
const { html, text, subject } = buildEmail(event, "rescheduled")
const recipients: string[] = []
if (event.creatorEmail) recipients.push(event.creatorEmail)
if (event.participantEmail && !recipients.includes(event.participantEmail)) recipients.push(event.participantEmail)
if (ADMIN_EMAIL && !recipients.includes(ADMIN_EMAIL)) recipients.push(ADMIN_EMAIL)
for (const to of recipients) {
await query(
`INSERT INTO sent_emails (recipient, subject, body_html, body_text)
VALUES ($1, $2, $3, $4)`,
[to, subject, html, text],
).catch(() => {})
}
if (recipients.length === 0) return
if (SMTP_HOST) {
const transporter = createTransporter()
await transporter.sendMail({
from: EMAIL_FROM,
to: recipients.join(", "),
subject,
text,
html,
}).catch(() => {})
return
}
console.log("=".repeat(60))
console.log(`🔄 Reschedule email ready — ${subject}`)
console.log(` To: ${recipients.join(", ")}`)
console.log(` Body:\n${text}`)
console.log("=".repeat(60))
}