100 lines
No EOL
3.9 KiB
TypeScript
100 lines
No EOL
3.9 KiB
TypeScript
import nodemailer from 'nodemailer'
|
|
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'
|
|
import type { Subscription } from './alert.js'
|
|
import { render } from './templates.js'
|
|
|
|
const secure = Number(SMTP_PORT) === 465
|
|
|
|
const transporter = nodemailer.createTransport({
|
|
host: SMTP_HOST,
|
|
port: Number(SMTP_PORT),
|
|
secure,
|
|
requireTLS: !secure,
|
|
auth: {
|
|
user: SMTP_USER,
|
|
pass: SMTP_PASSWORD,
|
|
},
|
|
})
|
|
|
|
export const sendConfirm = (sub: Subscription) => {
|
|
const href = `${BASE_URL}/confirm?token=${sub.key}`
|
|
const settingsUrl = `${EVENT_VIEWER_URL.replace(/\/$/, '')}/settings/alerts`
|
|
|
|
return transporter
|
|
.sendMail({
|
|
from: SMTP_FROM,
|
|
to: sub.email,
|
|
subject: `Confirm your email for ${BRAND_NAME} notifications`,
|
|
html: `
|
|
<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;">
|
|
${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>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<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>
|
|
<p style="margin:0;text-align:center;">
|
|
<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>
|
|
</p>
|
|
<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>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
`,
|
|
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}`,
|
|
})
|
|
.catch(error => {
|
|
console.error('mailer: confirmation email failed', {
|
|
to: sub.email,
|
|
smtp: { host: SMTP_HOST, port: SMTP_PORT },
|
|
error: error?.message || error,
|
|
})
|
|
throw error
|
|
})
|
|
}
|
|
|
|
export const sendDigest = async (sub: Subscription, variables: Record<string, any>) => {
|
|
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}`,
|
|
brandName: BRAND_NAME,
|
|
brandAccent: BRAND_ACCENT,
|
|
brandLogo: BRAND_LOGO,
|
|
settingsUrl: `${EVENT_VIEWER_URL.replace(/\/$/, '')}/settings/alerts`,
|
|
}),
|
|
})
|
|
.catch(error => {
|
|
console.error('mailer: digest email failed', {
|
|
to: sub.email,
|
|
smtp: { host: SMTP_HOST, port: SMTP_PORT },
|
|
error: error?.message || error,
|
|
})
|
|
throw error
|
|
})
|
|
} |