Removes resend npm dependency. Adds lib/notifications/mailer.ts with a raw-HTTP Brevo wrapper (sendEmail + sendPasswordResetEmail). All three notification files updated to use the new wrapper. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
63 lines
1.8 KiB
TypeScript
63 lines
1.8 KiB
TypeScript
import 'server-only'
|
|
|
|
interface MailOptions {
|
|
to: string | string[]
|
|
subject: string
|
|
html: string
|
|
text?: string
|
|
}
|
|
|
|
export async function sendEmail({ to, subject, html, text }: MailOptions): Promise<void> {
|
|
const apiKey = process.env.BREVO_API_KEY
|
|
if (!apiKey) {
|
|
console.error('BREVO_API_KEY not set — email not sent')
|
|
return
|
|
}
|
|
|
|
const fromEmail = process.env.BREVO_FROM_EMAIL ?? 'noreply@setia.com.my'
|
|
const fromName = process.env.BREVO_FROM_NAME ?? 'Setia IMS'
|
|
const recipients = Array.isArray(to) ? to : [to]
|
|
|
|
const body = {
|
|
sender: { name: fromName, email: fromEmail },
|
|
to: recipients.map(email => ({ email })),
|
|
subject,
|
|
htmlContent: html,
|
|
...(text ? { textContent: text } : {}),
|
|
}
|
|
|
|
const res = await fetch('https://api.brevo.com/v3/smtp/email', {
|
|
method: 'POST',
|
|
headers: {
|
|
'api-key': apiKey,
|
|
'Content-Type': 'application/json',
|
|
Accept: 'application/json',
|
|
},
|
|
body: JSON.stringify(body),
|
|
})
|
|
|
|
if (!res.ok) {
|
|
const detail = await res.text().catch(() => '')
|
|
console.error(`Brevo send failed (${res.status}): ${detail}`)
|
|
}
|
|
}
|
|
|
|
interface PasswordResetOptions {
|
|
to: string
|
|
name: string | null
|
|
resetLink: string
|
|
}
|
|
|
|
export async function sendPasswordResetEmail({ to, name, resetLink }: PasswordResetOptions): Promise<void> {
|
|
const displayName = name ?? 'User'
|
|
const subject = '[IMS] Password Reset Request'
|
|
const html = `
|
|
<p>Hi ${displayName},</p>
|
|
<p>You requested a password reset for your IMS account.</p>
|
|
<p><a href="${resetLink}">Reset your password</a></p>
|
|
<p>This link expires in 1 hour. If you did not request this, you can safely ignore this email.</p>
|
|
`
|
|
const text = `Hi ${displayName},\n\nReset your IMS password: ${resetLink}\n\nThis link expires in 1 hour.`
|
|
await sendEmail({ to, subject, html, text })
|
|
}
|