feat: Wabenhain Honig-Shop Anwendung
Vollstaendige Next.js/Prisma-E-Commerce-Anwendung fuer eine Imkerei: Shop, Warenkorb, Stripe-Checkout, Kundenkonto mit 2FA, Admin-Bereich (Produkte, Ernten, Chargen, Bestellungen, Gutscheine, admin-editierbare Website-Inhalte), i18n (DE/EN), Rate-Limiting/CSP/Audit-Log-Haertung, PostgreSQL- und MySQL-Unterstuetzung ueber austauschbare Prisma- Treiber-Adapter, Unit-/Komponenten-/E2E-Tests.
This commit is contained in:
1 parent
5c76fcc266
commit
c7927f2883
240 files changed
+26096
-137
No files matched your search
@@ -0,0 +1,74 @@
|
||||
import nodemailer from "nodemailer";
|
||||
|
||||
interface SendMailInput {
|
||||
to: string;
|
||||
subject: string;
|
||||
html: string;
|
||||
text: string;
|
||||
}
|
||||
|
||||
/** Maskiert eine E-Mail-Adresse fuer Log-Ausgaben, z.B. "ma***@example.com". */
|
||||
function maskEmail(email: string): string {
|
||||
const [local, domain] = email.split("@");
|
||||
if (!domain) return "***";
|
||||
return `${local.slice(0, 2)}***@${domain}`;
|
||||
}
|
||||
|
||||
let cachedTransport: ReturnType<typeof nodemailer.createTransport> | null = null;
|
||||
// Nur einmal geprueft (nicht bei jedem sendMail-Aufruf) — macht ein in
|
||||
// Produktion fehlendes SMTP-Setup im Server-Log sichtbar, statt dass
|
||||
// Bestellbestaetigungen/Passwort-Resets fortlaufend lautlos verschwinden.
|
||||
let warnedAboutMissingSmtpInProduction = false;
|
||||
|
||||
function getTransport() {
|
||||
if (cachedTransport) return cachedTransport;
|
||||
|
||||
const host = process.env.SMTP_HOST;
|
||||
const port = Number(process.env.SMTP_PORT ?? 587);
|
||||
const user = process.env.SMTP_USER;
|
||||
const pass = process.env.SMTP_PASSWORD;
|
||||
if (!host || !user || !pass) {
|
||||
if (process.env.NODE_ENV === "production" && !warnedAboutMissingSmtpInProduction) {
|
||||
warnedAboutMissingSmtpInProduction = true;
|
||||
console.warn(
|
||||
"SMTP_HOST/SMTP_USER/SMTP_PASSWORD sind in Produktion nicht gesetzt — Bestellbestaetigungen, Passwort-Resets und weitere Transaktions-E-Mails werden nur geloggt, nicht versendet."
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
cachedTransport = nodemailer.createTransport({
|
||||
host,
|
||||
port,
|
||||
secure: port === 465,
|
||||
auth: { user, pass },
|
||||
});
|
||||
return cachedTransport;
|
||||
}
|
||||
|
||||
/**
|
||||
* Versendet eine E-Mail ueber den in SMTP_HOST/SMTP_USER/SMTP_PASSWORD
|
||||
* konfigurierten Mailserver. Ohne Konfiguration wird die Mail NICHT
|
||||
* gesendet, sondern nur geloggt — analog zum Stripe-Fallback ohne Keys,
|
||||
* damit lokale Entwicklung und Deployments ohne Mail-Setup nicht abstuerzen.
|
||||
* Wirft absichtlich keinen Fehler: ein Mail-Ausfall darf eine Bestellung
|
||||
* oder Registrierung nicht verhindern.
|
||||
*/
|
||||
export async function sendMail(input: SendMailInput): Promise<void> {
|
||||
const transport = getTransport();
|
||||
const from = process.env.SMTP_FROM || "Wabenhain <[email protected]>";
|
||||
|
||||
if (!transport) {
|
||||
console.warn(
|
||||
`[mail] SMTP nicht konfiguriert — E-Mail an ${maskEmail(input.to)} ("${input.subject}") wurde nicht gesendet.`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await transport.sendMail({ from, to: input.to, subject: input.subject, html: input.html, text: input.text });
|
||||
} catch (error: unknown) {
|
||||
const message = error instanceof Error ? error.message : "Unbekannter Fehler";
|
||||
console.error(`[mail] Versand an ${maskEmail(input.to)} fehlgeschlagen: ${message}`);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user