Add SMTP admin settings and contact form delivery
This commit is contained in:
+172
@@ -0,0 +1,172 @@
|
||||
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,
|
||||
},
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user