212 lines
8.5 KiB
TypeScript
212 lines
8.5 KiB
TypeScript
import nodemailer from "nodemailer";
|
||
import { queueEmail } from "./email-queue";
|
||
|
||
// Port 587 = STARTTLS, Port 465 = SSL/TLS
|
||
const transporter = nodemailer.createTransport({
|
||
host: process.env.SMTP_HOST,
|
||
port: Number(process.env.SMTP_PORT ?? 587),
|
||
secure: false,
|
||
auth: {
|
||
user: process.env.SMTP_USER,
|
||
pass: process.env.SMTP_PASS,
|
||
},
|
||
connectionTimeout: 15000,
|
||
greetingTimeout: 10000,
|
||
socketTimeout: 20000,
|
||
tls: {
|
||
rejectUnauthorized: false,
|
||
ciphers: "SSLv3",
|
||
},
|
||
});
|
||
|
||
export interface KontaktEmailData {
|
||
name: string;
|
||
anrede?: string;
|
||
telefon?: string;
|
||
email: string;
|
||
betreff: string;
|
||
nachricht?: string;
|
||
}
|
||
|
||
interface MailOptions {
|
||
from: string;
|
||
to: string | undefined;
|
||
replyTo?: string;
|
||
subject: string;
|
||
text: string;
|
||
html: string;
|
||
}
|
||
|
||
async function sendWithFallback(
|
||
options: MailOptions,
|
||
label: string
|
||
): Promise<{ sent: boolean; queued: boolean }> {
|
||
if (!options.to) {
|
||
console.error(`[Mailer] Kein Empfänger für "${label}" – Mail übersprungen`);
|
||
return { sent: false, queued: false };
|
||
}
|
||
try {
|
||
await transporter.sendMail(options);
|
||
console.log(`[Mailer] ✓ Mail "${label}" an "${options.to}" gesendet`);
|
||
return { sent: true, queued: false };
|
||
} catch (err) {
|
||
const msg = err instanceof Error ? err.message : String(err);
|
||
console.error(`[Mailer] ✗ Mail "${label}" fehlgeschlagen (${msg}) – versuche Queue`);
|
||
const queued = await queueEmail({
|
||
mail_from: options.from,
|
||
mail_to: options.to,
|
||
reply_to: options.replyTo,
|
||
subject: options.subject,
|
||
html: options.html,
|
||
body_text: options.text,
|
||
});
|
||
return { sent: false, queued };
|
||
}
|
||
}
|
||
|
||
export interface RegistrierungData {
|
||
email: string;
|
||
firma?: string;
|
||
bestaetigungsLink: string;
|
||
}
|
||
|
||
export async function sendeRegistrierungsBestaetigung(
|
||
data: RegistrierungData
|
||
): Promise<void> {
|
||
const html = `
|
||
<!DOCTYPE html>
|
||
<html lang="de">
|
||
<head><meta charset="UTF-8"></head>
|
||
<body style="font-family:system-ui,sans-serif;color:#e2e8f0;max-width:600px;margin:0 auto;padding:0">
|
||
<div style="background:#18212f;padding:20px 24px;border-bottom:2px solid #f97316">
|
||
<h1 style="color:#f97316;margin:0;font-size:20px;font-weight:700">MBO Tech IT</h1>
|
||
<p style="color:rgba(255,255,255,0.6);margin:4px 0 0;font-size:13px">Kunden-Portal</p>
|
||
</div>
|
||
<div style="padding:24px;background:#1e2a3b">
|
||
<h2 style="margin:0 0 16px;font-size:18px;color:#f8fafc">Bitte bestätigen Sie Ihre E-Mail-Adresse</h2>
|
||
<p style="color:#94a3b8;margin:0 0 24px">
|
||
${data.firma ? `Hallo ${data.firma},` : "Hallo,"}<br><br>
|
||
vielen Dank für Ihre Registrierung im MBO Tech IT Kunden-Portal.
|
||
Bitte bestätigen Sie Ihre E-Mail-Adresse, um Zugang zu Ihren IT-Anfragen zu erhalten.
|
||
</p>
|
||
<a href="${data.bestaetigungsLink}"
|
||
style="display:inline-block;background:#f97316;color:#fff;font-weight:700;padding:12px 24px;border-radius:8px;text-decoration:none;font-size:15px">
|
||
E-Mail bestätigen
|
||
</a>
|
||
<p style="color:#64748b;font-size:12px;margin:24px 0 0">
|
||
Dieser Link ist 24 Stunden gültig. Falls Sie sich nicht registriert haben, können Sie diese E-Mail ignorieren.
|
||
</p>
|
||
</div>
|
||
<div style="padding:12px 24px;background:#111925;border-top:1px solid rgba(255,255,255,0.1)">
|
||
<p style="margin:0;font-size:11px;color:#64748b">MBO Tech IT · ${process.env.APP_URL ?? "https://mbo-tech-it.de"}</p>
|
||
</div>
|
||
</body>
|
||
</html>`;
|
||
|
||
await sendWithFallback(
|
||
{
|
||
from: `"MBO Tech IT" <${process.env.SMTP_FROM}>`,
|
||
to: data.email,
|
||
subject: "Bitte bestätigen Sie Ihre E-Mail – MBO Tech IT",
|
||
text: `Hallo,\n\nBitte bestätigen Sie Ihre E-Mail-Adresse:\n${data.bestaetigungsLink}\n\nDieser Link ist 24 Stunden gültig.\n\nMBO Tech IT`,
|
||
html,
|
||
},
|
||
`Registrierungsbestätigung ${data.email}`
|
||
);
|
||
}
|
||
|
||
export async function sendeAnfrageBestaetigung(data: {
|
||
name: string;
|
||
email: string;
|
||
betreff: string;
|
||
}): Promise<void> {
|
||
const html = `
|
||
<!DOCTYPE html>
|
||
<html lang="de">
|
||
<head><meta charset="UTF-8"></head>
|
||
<body style="font-family:system-ui,sans-serif;color:#e2e8f0;max-width:600px;margin:0 auto;padding:0">
|
||
<div style="background:#18212f;padding:20px 24px;border-bottom:2px solid #f97316">
|
||
<h1 style="color:#f97316;margin:0;font-size:20px;font-weight:700">MBO Tech IT</h1>
|
||
<p style="color:rgba(255,255,255,0.6);margin:4px 0 0;font-size:13px">Anfrage erhalten</p>
|
||
</div>
|
||
<div style="padding:24px;background:#1e2a3b">
|
||
<h2 style="margin:0 0 16px;font-size:18px;color:#f8fafc">Vielen Dank für Ihre Anfrage, ${data.name}!</h2>
|
||
<p style="color:#94a3b8;margin:0 0 16px;line-height:1.6">
|
||
Ihre Anfrage zum Thema <strong style="color:#f8fafc">„${data.betreff}"</strong> ist bei uns eingegangen.
|
||
Wir melden uns schnellstmöglich bei Ihnen.
|
||
</p>
|
||
<p style="color:#94a3b8;margin:0 0 24px;line-height:1.6">
|
||
Bei dringenden Anliegen erreichen Sie uns direkt unter:<br>
|
||
<a href="tel:+4917193451093" style="color:#f97316;font-weight:600">+49 171 9345193</a>
|
||
</p>
|
||
<div style="padding:12px 16px;background:#111925;border-left:3px solid #f97316;border-radius:0 4px 4px 0">
|
||
<p style="margin:0;font-size:13px;color:#64748b">Betreff: ${data.betreff}</p>
|
||
</div>
|
||
</div>
|
||
<div style="padding:12px 24px;background:#111925;border-top:1px solid rgba(255,255,255,0.1)">
|
||
<p style="margin:0;font-size:11px;color:#64748b">MBO Tech IT · <a href="${process.env.APP_URL ?? "https://mbo-tech-it.de"}" style="color:#64748b">${process.env.APP_URL ?? "https://mbo-tech-it.de"}</a></p>
|
||
</div>
|
||
</body>
|
||
</html>`;
|
||
|
||
await sendWithFallback(
|
||
{
|
||
from: `"MBO Tech IT" <${process.env.SMTP_FROM}>`,
|
||
to: data.email,
|
||
subject: `Ihre Anfrage ist eingegangen – MBO Tech IT`,
|
||
text: `Hallo ${data.name},\n\nvielen Dank für Ihre Anfrage zum Thema „${data.betreff}". Wir melden uns schnellstmöglich bei Ihnen.\n\nBei dringenden Anliegen erreichen Sie uns unter: +49 171 9345193\n\nMBO Tech IT\n${process.env.APP_URL ?? "https://mbo-tech-it.de"}`,
|
||
html,
|
||
},
|
||
`Anfragebestätigung ${data.email}`
|
||
);
|
||
}
|
||
|
||
export async function sendeKontaktEmail(
|
||
data: KontaktEmailData
|
||
): Promise<{ sent: boolean; queued: boolean }> {
|
||
const anrede = data.anrede
|
||
? `${data.anrede.charAt(0).toUpperCase() + data.anrede.slice(1)} `
|
||
: "";
|
||
|
||
const html = `
|
||
<!DOCTYPE html>
|
||
<html lang="de">
|
||
<head><meta charset="UTF-8"></head>
|
||
<body style="font-family:system-ui,sans-serif;color:#e2e8f0;max-width:600px;margin:0 auto;padding:0">
|
||
<div style="background:#18212f;padding:20px 24px;border-bottom:2px solid #f97316">
|
||
<h1 style="color:#f97316;margin:0;font-size:20px;font-weight:700">MBO Tech IT</h1>
|
||
<p style="color:rgba(255,255,255,0.6);margin:4px 0 0;font-size:13px">Neue Kontaktanfrage</p>
|
||
</div>
|
||
<div style="padding:24px;background:#1e2a3b">
|
||
<h2 style="margin:0 0 16px;font-size:18px;color:#f8fafc">Kontaktanfrage von ${anrede}${data.name}</h2>
|
||
<table style="width:100%;border-collapse:collapse;margin-bottom:24px">
|
||
<tr><td style="padding:6px 0;color:#94a3b8;width:120px">Name</td><td style="padding:6px 0;font-weight:600;color:#f8fafc">${anrede}${data.name}</td></tr>
|
||
${data.telefon ? `<tr><td style="padding:6px 0;color:#94a3b8">Telefon</td><td style="padding:6px 0"><a href="tel:${data.telefon}" style="color:#f97316">${data.telefon}</a></td></tr>` : ""}
|
||
<tr><td style="padding:6px 0;color:#94a3b8">E-Mail</td><td style="padding:6px 0"><a href="mailto:${data.email}" style="color:#60a5fa">${data.email}</a></td></tr>
|
||
<tr><td style="padding:6px 0;color:#94a3b8">Betreff</td><td style="padding:6px 0;color:#f8fafc">${data.betreff}</td></tr>
|
||
</table>
|
||
${
|
||
data.nachricht
|
||
? `<div style="padding:12px 16px;background:#111925;border-left:3px solid #f97316"><p style="margin:0;font-size:14px;white-space:pre-wrap;color:#cbd5e1">${data.nachricht}</p></div>`
|
||
: ""
|
||
}
|
||
</div>
|
||
<div style="padding:12px 24px;background:#111925;border-top:1px solid rgba(255,255,255,0.1)">
|
||
<p style="margin:0;font-size:11px;color:#64748b">MBO Tech IT · ${process.env.APP_URL ?? "https://mbo-tech-it.de"}</p>
|
||
</div>
|
||
</body>
|
||
</html>`;
|
||
|
||
return sendWithFallback(
|
||
{
|
||
from: `"MBO Tech IT" <${process.env.SMTP_FROM}>`,
|
||
to: process.env.SMTP_TO,
|
||
replyTo: data.email,
|
||
subject: `Kontaktanfrage: ${anrede}${data.name} – ${data.betreff}`,
|
||
text: `Neue Kontaktanfrage\n\nName: ${anrede}${data.name}${data.telefon ? `\nTelefon: ${data.telefon}` : ""}\nE-Mail: ${data.email}\nBetreff: ${data.betreff}${data.nachricht ? `\n\nNachricht:\n${data.nachricht}` : ""}`,
|
||
html,
|
||
},
|
||
`Kontaktanfrage ${data.name}`
|
||
);
|
||
}
|