173 lines
4.3 KiB
TypeScript
173 lines
4.3 KiB
TypeScript
import nodemailer, { type SendMailOptions } from "nodemailer";
|
|
|
|
import { getMailSettings } from "@/lib/app-config";
|
|
import type { AppLocale } from "@/lib/locale";
|
|
import type { MailSettings } from "@/lib/mail-settings";
|
|
|
|
type MailTransportOptions = {
|
|
host: string;
|
|
port: number;
|
|
secure: boolean;
|
|
auth: {
|
|
user: string;
|
|
pass: string;
|
|
};
|
|
};
|
|
|
|
type MailTransport = {
|
|
sendMail: (options: SendMailOptions) => Promise<unknown>;
|
|
};
|
|
|
|
type CreateTransport = (options: MailTransportOptions) => MailTransport;
|
|
|
|
type SendMailDeps = {
|
|
settings?: MailSettings;
|
|
createTransport?: CreateTransport;
|
|
};
|
|
|
|
type SendMailInput = {
|
|
to: string;
|
|
subject: string;
|
|
text: string;
|
|
replyTo?: string;
|
|
};
|
|
|
|
type ContactMessageInput = {
|
|
name: string;
|
|
email: string;
|
|
phone?: string;
|
|
company?: string;
|
|
message: string;
|
|
locale: AppLocale;
|
|
};
|
|
|
|
function requireValue(value: string, message: string) {
|
|
if (!value.trim()) {
|
|
throw new Error(message);
|
|
}
|
|
|
|
return value.trim();
|
|
}
|
|
|
|
function getConfiguredTransportSettings(settings: MailSettings) {
|
|
const smtpHost = requireValue(settings.smtp.host, "SMTP host is required.");
|
|
const smtpUsername = requireValue(settings.smtp.username, "SMTP username is required.");
|
|
const smtpPassword = requireValue(settings.smtp.password, "SMTP password is required.");
|
|
const fromEmail = requireValue(settings.sender.email, "From email is required.");
|
|
|
|
return {
|
|
smtpHost,
|
|
smtpUsername,
|
|
smtpPassword,
|
|
fromEmail,
|
|
};
|
|
}
|
|
|
|
function getConfiguredRecipient(settings: MailSettings, recipientKey: "contact" | "test") {
|
|
if (recipientKey === "contact") {
|
|
return requireValue(
|
|
settings.recipients.contact || settings.recipients.test,
|
|
"Contact recipient email is required.",
|
|
);
|
|
}
|
|
|
|
return requireValue(
|
|
settings.recipients.test || settings.recipients.contact,
|
|
"Test recipient email is required.",
|
|
);
|
|
}
|
|
|
|
export function createSmtpTransport(
|
|
settings: MailSettings,
|
|
createTransport: CreateTransport = nodemailer.createTransport,
|
|
) {
|
|
const { smtpHost, smtpUsername, smtpPassword } = getConfiguredTransportSettings(settings);
|
|
|
|
return createTransport({
|
|
host: smtpHost,
|
|
port: settings.smtp.port,
|
|
secure: settings.smtp.secure,
|
|
auth: {
|
|
user: smtpUsername,
|
|
pass: smtpPassword,
|
|
},
|
|
});
|
|
}
|
|
|
|
export async function sendMail(
|
|
input: SendMailInput,
|
|
deps: SendMailDeps = {},
|
|
) {
|
|
const settings = deps.settings ?? await getMailSettings();
|
|
const { fromEmail } = getConfiguredTransportSettings(settings);
|
|
const transport = createSmtpTransport(settings, deps.createTransport);
|
|
|
|
await transport.sendMail({
|
|
from: settings.sender.name ? `${settings.sender.name} <${fromEmail}>` : fromEmail,
|
|
to: input.to,
|
|
subject: input.subject,
|
|
text: input.text,
|
|
replyTo: input.replyTo,
|
|
});
|
|
}
|
|
|
|
export async function sendContactMessage(
|
|
input: ContactMessageInput,
|
|
deps: SendMailDeps = {},
|
|
) {
|
|
const settings = deps.settings ?? await getMailSettings();
|
|
const recipient = getConfiguredRecipient(settings, "contact");
|
|
|
|
await sendMail(
|
|
{
|
|
to: recipient,
|
|
subject: "New contact message",
|
|
replyTo: input.email,
|
|
text: [
|
|
"A new contact message was submitted.",
|
|
"",
|
|
`Name: ${input.name}`,
|
|
`Email: ${input.email}`,
|
|
`Phone: ${input.phone?.trim() || "-"}`,
|
|
`Company: ${input.company?.trim() || "-"}`,
|
|
`Locale: ${input.locale}`,
|
|
`Submitted at: ${new Date().toISOString()}`,
|
|
"",
|
|
"Message:",
|
|
input.message,
|
|
].join("\n"),
|
|
},
|
|
{
|
|
settings,
|
|
createTransport: deps.createTransport,
|
|
},
|
|
);
|
|
}
|
|
|
|
export async function sendTestEmail(
|
|
deps: SendMailDeps = {},
|
|
) {
|
|
const settings = deps.settings ?? await getMailSettings();
|
|
const recipient = getConfiguredRecipient(settings, "test");
|
|
const { fromEmail } = getConfiguredTransportSettings(settings);
|
|
|
|
await sendMail(
|
|
{
|
|
to: recipient,
|
|
subject: "SMTP test email",
|
|
text: [
|
|
"This is a backend SMTP test email.",
|
|
"",
|
|
`Timestamp: ${new Date().toISOString()}`,
|
|
`Configured from email: ${fromEmail}`,
|
|
`Configured recipient: ${recipient}`,
|
|
`Environment: ${process.env.NODE_ENV ?? "development"}`,
|
|
].join("\n"),
|
|
},
|
|
{
|
|
settings,
|
|
createTransport: deps.createTransport,
|
|
},
|
|
);
|
|
}
|