import { createTransport } from "nodemailer"; import serverConfig from "@karakeep/shared/config"; export async function sendVerificationEmail( email: string, name: string, token: string, ) { if (!serverConfig.email.smtp) { throw new Error("SMTP is not configured"); } const transporter = createTransport({ host: serverConfig.email.smtp.host, port: serverConfig.email.smtp.port, secure: serverConfig.email.smtp.secure, auth: serverConfig.email.smtp.user && serverConfig.email.smtp.password ? { user: serverConfig.email.smtp.user, pass: serverConfig.email.smtp.password, } : undefined, }); const verificationUrl = `${serverConfig.publicUrl}/verify-email?token=${encodeURIComponent(token)}&email=${encodeURIComponent(email)}`; const mailOptions = { from: serverConfig.email.smtp.from, to: email, subject: "Verify your email address", html: `

Welcome to Karakeep, ${name}!

Please verify your email address by clicking the link below:

Verify Email Address

If the button doesn't work, you can copy and paste this link into your browser:

${verificationUrl}

This link will expire in 24 hours.

If you didn't create an account with us, please ignore this email.

`, text: ` Welcome to Karakeep, ${name}! Please verify your email address by visiting this link: ${verificationUrl} This link will expire in 24 hours. If you didn't create an account with us, please ignore this email. `, }; await transporter.sendMail(mailOptions); } export async function sendInviteEmail( email: string, token: string, inviterName: string, ) { if (!serverConfig.email.smtp) { throw new Error("SMTP is not configured"); } const transporter = createTransport({ host: serverConfig.email.smtp.host, port: serverConfig.email.smtp.port, secure: serverConfig.email.smtp.secure, auth: serverConfig.email.smtp.user && serverConfig.email.smtp.password ? { user: serverConfig.email.smtp.user, pass: serverConfig.email.smtp.password, } : undefined, }); const inviteUrl = `${serverConfig.publicUrl}/invite/${encodeURIComponent(token)}`; const mailOptions = { from: serverConfig.email.smtp.from, to: email, subject: "You've been invited to join Karakeep", html: `

You've been invited to join Karakeep!

${inviterName} has invited you to join Karakeep, the bookmark everything app.

Click the link below to accept your invitation and create your account:

Accept Invitation

If the button doesn't work, you can copy and paste this link into your browser:

${inviteUrl}

If you weren't expecting this invitation, you can safely ignore this email.

`, text: ` You've been invited to join Karakeep! ${inviterName} has invited you to join Karakeep, a powerful bookmarking and content organization platform. Accept your invitation by visiting this link: ${inviteUrl} If you weren't expecting this invitation, you can safely ignore this email. `, }; await transporter.sendMail(mailOptions); } export async function sendPasswordResetEmail( email: string, name: string, token: string, ) { if (!serverConfig.email.smtp) { throw new Error("SMTP is not configured"); } const transporter = createTransport({ host: serverConfig.email.smtp.host, port: serverConfig.email.smtp.port, secure: serverConfig.email.smtp.secure, auth: serverConfig.email.smtp.user && serverConfig.email.smtp.password ? { user: serverConfig.email.smtp.user, pass: serverConfig.email.smtp.password, } : undefined, }); const resetUrl = `${serverConfig.publicUrl}/reset-password?token=${encodeURIComponent(token)}`; const mailOptions = { from: serverConfig.email.smtp.from, to: email, subject: "Reset your password", html: `

Password Reset Request

Hi ${name},

You requested to reset your password for your Karakeep account. Click the link below to reset your password:

Reset Password

If the button doesn't work, you can copy and paste this link into your browser:

${resetUrl}

This link will expire in 1 hour.

If you didn't request a password reset, please ignore this email. Your password will remain unchanged.

`, text: ` Hi ${name}, You requested to reset your password for your Karakeep account. Visit this link to reset your password: ${resetUrl} This link will expire in 1 hour. If you didn't request a password reset, please ignore this email. Your password will remain unchanged. `, }; await transporter.sendMail(mailOptions); } export async function sendListInvitationEmail( email: string, inviterName: string, listName: string, listId: string, ) { if (!serverConfig.email.smtp) { // Silently fail if email is not configured return; } const transporter = createTransport({ host: serverConfig.email.smtp.host, port: serverConfig.email.smtp.port, secure: serverConfig.email.smtp.secure, auth: serverConfig.email.smtp.user && serverConfig.email.smtp.password ? { user: serverConfig.email.smtp.user, pass: serverConfig.email.smtp.password, } : undefined, }); const inviteUrl = `${serverConfig.publicUrl}/dashboard/lists?pendingInvitation=${encodeURIComponent(listId)}`; const mailOptions = { from: serverConfig.email.smtp.from, to: email, subject: `${inviterName} invited you to collaborate on "${listName}"`, html: `

You've been invited to collaborate on a list!

${inviterName} has invited you to collaborate on the list "${listName}" in Karakeep.

Click the link below to view and accept or decline the invitation:

View Invitation

If the button doesn't work, you can copy and paste this link into your browser:

${inviteUrl}

You can accept or decline this invitation from your Karakeep dashboard.

If you weren't expecting this invitation, you can safely ignore this email or decline it in your dashboard.

`, text: ` You've been invited to collaborate on a list! ${inviterName} has invited you to collaborate on the list "${listName}" in Karakeep. View your invitation by visiting this link: ${inviteUrl} You can accept or decline this invitation from your Karakeep dashboard. If you weren't expecting this invitation, you can safely ignore this email or decline it in your dashboard. `, }; await transporter.sendMail(mailOptions); }