feat(mail): add smtp email delivery
This commit is contained in:
parent
faddaa91d0
commit
3456b04ed4
25 changed files with 529 additions and 60 deletions
100
lib/mail/smtp.ts
Normal file
100
lib/mail/smtp.ts
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
import nodemailer from "nodemailer";
|
||||
import type SMTPTransport from "nodemailer/lib/smtp-transport";
|
||||
import type { SmtpSettings } from "@/lib/admin/types";
|
||||
import { getSmtpSettings, isSmtpConfigured, updateSmtpDeliveryStatus, updateSmtpTestStatus } from "./config";
|
||||
import { contactTemplate, repairTemplate, testTemplate } from "./templates";
|
||||
import type { ContactMailInput, MailSendResult, RepairMailInput } from "./types";
|
||||
|
||||
function sanitizeMailError(error: unknown) {
|
||||
const message = error instanceof Error ? error.message : "Unbekannter SMTP-Fehler";
|
||||
return message
|
||||
.replace(/AUTH PLAIN\s+\S+/gi, "AUTH PLAIN [redacted]")
|
||||
.replace(/AUTH LOGIN\s+\S+/gi, "AUTH LOGIN [redacted]")
|
||||
.replace(/password[=:]\S+/gi, "password=[redacted]")
|
||||
.slice(0, 500);
|
||||
}
|
||||
|
||||
function createTransport(settings: SmtpSettings) {
|
||||
const secure = settings.security === "tls";
|
||||
const requireTLS = settings.security === "starttls";
|
||||
const options: SMTPTransport.Options = {
|
||||
host: settings.host,
|
||||
port: Number(settings.port),
|
||||
secure,
|
||||
requireTLS,
|
||||
auth: {
|
||||
user: settings.username,
|
||||
pass: settings.password,
|
||||
},
|
||||
};
|
||||
|
||||
if (settings.security === "none") {
|
||||
options.auth = settings.username && settings.password ? options.auth : undefined;
|
||||
options.ignoreTLS = true;
|
||||
}
|
||||
|
||||
return nodemailer.createTransport(options);
|
||||
}
|
||||
|
||||
async function sendConfiguredMail(input: {
|
||||
subject: string;
|
||||
text: string;
|
||||
html: string;
|
||||
replyTo?: string;
|
||||
}): Promise<MailSendResult> {
|
||||
const settings = await getSmtpSettings();
|
||||
if (!isSmtpConfigured(settings)) return { ok: false, error: "SMTP nicht konfiguriert." };
|
||||
|
||||
try {
|
||||
const transport = createTransport(settings);
|
||||
await transport.sendMail({
|
||||
from: settings.fromAddress,
|
||||
to: settings.recipientAddress,
|
||||
bcc: settings.bccAddress || undefined,
|
||||
replyTo: input.replyTo || settings.replyToAddress || undefined,
|
||||
subject: input.subject,
|
||||
text: input.text,
|
||||
html: input.html,
|
||||
});
|
||||
return { ok: true };
|
||||
} catch (error) {
|
||||
return { ok: false, error: sanitizeMailError(error) };
|
||||
}
|
||||
}
|
||||
|
||||
export async function sendTestMail() {
|
||||
const template = testTemplate();
|
||||
const result = await sendConfiguredMail(template);
|
||||
await updateSmtpTestStatus({
|
||||
lastTestAt: new Date().toISOString(),
|
||||
lastTestStatus: result.ok ? "success" : "error",
|
||||
lastError: result.ok ? "" : result.error,
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function sendContactInquiryMail(input: ContactMailInput) {
|
||||
const template = contactTemplate(input);
|
||||
const result = await sendConfiguredMail({ ...template, replyTo: input.email });
|
||||
if (result.error !== "SMTP nicht konfiguriert.") {
|
||||
await updateSmtpDeliveryStatus({
|
||||
lastDeliveryAt: new Date().toISOString(),
|
||||
lastDeliveryStatus: result.ok ? "success" : "error",
|
||||
lastError: result.ok ? "" : result.error,
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function sendRepairInquiryMail(input: RepairMailInput) {
|
||||
const template = repairTemplate(input);
|
||||
const result = await sendConfiguredMail({ ...template, replyTo: input.email });
|
||||
if (result.error !== "SMTP nicht konfiguriert.") {
|
||||
await updateSmtpDeliveryStatus({
|
||||
lastDeliveryAt: new Date().toISOString(),
|
||||
lastDeliveryStatus: result.ok ? "success" : "error",
|
||||
lastError: result.ok ? "" : result.error,
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue