2026-08-24 20:12:32 +00:00
import nodemailer from 'nodemailer'
2026-08-26 21:57:26 +00:00
import {
SMTP_HOST ,
SMTP_PORT ,
SMTP_USER ,
SMTP_PASSWORD ,
SMTP_FROM ,
BASE_URL ,
EVENT_VIEWER_URL ,
BRAND_ACCENT ,
BRAND_NAME ,
BRAND_LOGO ,
} from './env.js'
2026-08-18 16:21:22 +00:00
import type { Subscription } from './alert.js'
import { render } from './templates.js'
2026-08-25 19:15:33 +00:00
const secure = Number ( SMTP_PORT ) === 465
2026-08-24 20:12:32 +00:00
const transporter = nodemailer . createTransport ( {
host : SMTP_HOST ,
port : Number ( SMTP_PORT ) ,
2026-08-25 19:15:33 +00:00
secure ,
requireTLS : ! secure ,
2026-08-24 20:12:32 +00:00
auth : {
user : SMTP_USER ,
pass : SMTP_PASSWORD ,
} ,
} )
2026-08-18 16:21:22 +00:00
export const sendConfirm = ( sub : Subscription ) = > {
const href = ` ${ BASE_URL } /confirm?token= ${ sub . key } `
2026-08-27 14:17:06 +00:00
const settingsUrl = ` ${ EVENT_VIEWER_URL . replace ( /\/$/ , '' ) } /settings/alerts `
2026-08-18 16:21:22 +00:00
2026-08-25 19:15:33 +00:00
return transporter
. sendMail ( {
from : SMTP_FROM ,
to : sub.email ,
2026-08-27 14:17:06 +00:00
subject : ` Confirm your email for ${ BRAND_NAME } notifications ` ,
2026-08-25 19:15:33 +00:00
html : `
2026-08-26 21:57:26 +00:00
< table role = "presentation" width = "100%" cellspacing = "0" cellpadding = "0" style = "background:#f0f2f5;padding:32px 16px;font-family:Inter,Helvetica,Arial,sans-serif;" >
< tr >
< td align = "center" >
< table role = "presentation" width = "100%" cellpadding = "0" cellspacing = "0" style = "max-width:480px;background:#ffffff;border-radius:12px;overflow:hidden;border:1px solid #e2e8f0;" >
< tr >
< td style = "padding:28px 32px 8px 32px;text-align:center;" >
2026-08-27 14:17:06 +00:00
$ { BRAND_LOGO ? ` <img src=" ${ BRAND_LOGO } " alt=" ${ BRAND_NAME } " width="40" style="width:40px;height:auto;display:block;margin:0 auto 12px;" /> ` : '' }
< h1 style = "margin:0;font-size:24px;color:#1e293b;font-weight:700;" > < span style = "color:${BRAND_ACCENT};" > $ { BRAND_NAME } < / span > < / h1 >
2026-08-26 21:57:26 +00:00
< / td >
< / tr >
< tr >
2026-08-27 14:17:06 +00:00
< td style = "padding:16px 32px 24px;color:#334155;font-size:15px;line-height:1.6;" >
< p style = "margin:0 0 12px;" > Please click below to confirm your email address . We ' ll send occasional emails with updates from your communities on $ { BRAND_NAME } . < / p >
2026-08-26 21:57:26 +00:00
< p style = "margin:0;text-align:center;" >
2026-08-27 14:17:06 +00:00
< a href = "${href}" style = "display:inline-block;background:${BRAND_ACCENT};color:#ffffff;padding:12px 28px;border-radius:8px;font-weight:600;text-decoration:none;" > Confirm < / a >
2026-08-26 21:57:26 +00:00
< / p >
2026-08-27 14:17:06 +00:00
< p style = "margin:16px 0 0;font-size:12px;color:#94a3b8;" > To disable notifications , ignore this email . Or update your settings at < a href = "${settingsUrl}" style = "color:${BRAND_ACCENT};" > $ { EVENT_VIEWER_URL . replace ( /^https?:\/\// , '' ) } / settings / alerts < / a > . < / p >
2026-08-26 21:57:26 +00:00
< / td >
< / tr >
< / table >
< / td >
< / tr >
< / table >
2026-08-18 16:21:22 +00:00
` ,
2026-08-27 14:17:06 +00:00
text : ` Please click below to receive emails for updates from your communities on ${ BRAND_NAME } . \ n \ nConfirm: ${ href } \ n \ nTo disable notifications, ignore this email. Or update your settings at: ${ settingsUrl } ` ,
2026-08-25 19:15:33 +00:00
} )
. catch ( error = > {
console . error ( 'mailer: confirmation email failed' , {
to : sub.email ,
smtp : { host : SMTP_HOST , port : SMTP_PORT } ,
error : error?.message || error ,
} )
throw error
} )
2026-08-18 16:21:22 +00:00
}
export const sendDigest = async ( sub : Subscription , variables : Record < string , any > ) = > {
2026-08-25 19:15:33 +00:00
return transporter
. sendMail ( {
from : SMTP_FROM ,
to : sub.email ,
subject : 'New activity' ,
html : await render ( 'emails/digest.mjml' , {
. . . variables ,
name : sub.email.split ( '@' ) [ 0 ] ,
unsubscribeUrl : ` ${ BASE_URL } /unsubscribe?token= ${ sub . key } ` ,
2026-08-26 21:57:26 +00:00
brandName : BRAND_NAME ,
brandAccent : BRAND_ACCENT ,
brandLogo : BRAND_LOGO ,
2026-08-25 19:15:33 +00:00
} ) ,
} )
. catch ( error = > {
console . error ( 'mailer: digest email failed' , {
to : sub.email ,
smtp : { host : SMTP_HOST , port : SMTP_PORT } ,
error : error?.message || error ,
} )
throw error
} )
2026-08-18 16:21:22 +00:00
}