151 lines
5.5 KiB
JavaScript
151 lines
5.5 KiB
JavaScript
import nodemailer from 'nodemailer'
|
|
import { config } from '../config.js'
|
|
import { renderMail } from './mailTemplates.js'
|
|
|
|
// Absendernamen wie im Design: Kundenmails "WEBklar", Hinweise ans Team "WEBklar Kundenportal"
|
|
const CUSTOMER_SENDER = 'WEBklar'
|
|
const TEAM_SENDER = 'WEBklar Kundenportal'
|
|
|
|
let transporter = null
|
|
let testTransport = null
|
|
|
|
/**
|
|
* Nur für Tests (F25): alle Mails über diesen Transport statt per SMTP, z. B. mailSink() aus
|
|
* test/support/mailSink.js (nodemailer jsonTransport, gesendete Mails gesammelt). null schaltet zurück.
|
|
* Gerendert wird wie im Betrieb; so lassen sich Vorlagen, Blöcke und Anhänge in Routentests prüfen.
|
|
*/
|
|
export function setMailTransportForTests(transport) {
|
|
testTransport = transport || null
|
|
}
|
|
|
|
function getTransporter() {
|
|
if (testTransport) return testTransport
|
|
if (!config.smtp.host || !config.smtp.user) return null
|
|
if (!transporter) {
|
|
transporter = nodemailer.createTransport({
|
|
host: config.smtp.host,
|
|
port: config.smtp.port,
|
|
secure: config.smtp.secure,
|
|
// Port 587: ohne STARTTLS nicht senden (Hetzner erlaubt die Anmeldung auch unverschluesselt)
|
|
requireTLS: !config.smtp.secure,
|
|
auth: { user: config.smtp.user, pass: config.smtp.pass },
|
|
})
|
|
}
|
|
return transporter
|
|
}
|
|
|
|
/** Vorlage rendern (server/mail-templates); Unstimmigkeiten landen im Log, die Mail geht trotzdem raus. */
|
|
function render({ template, values = {}, blocks = {} }) {
|
|
const mail = renderMail(template, values, blocks)
|
|
for (const problem of mail.problems) console.warn(`[mailer] Vorlage ${template}: ${problem}`)
|
|
return mail
|
|
}
|
|
|
|
// Throttle: pro Kunde maximal eine Benachrichtigung je Zeitfenster,
|
|
// damit ein aktiver Chat nicht pro Nachricht eine Mail ausloest.
|
|
const lastNotifyByCustomer = new Map() // customerId -> timestamp
|
|
|
|
/**
|
|
* Benachrichtigt die Admins per E-Mail ueber eine neue Portal-Chatnachricht.
|
|
* Fehler werden nur geloggt - der Chat darf am Mailversand nie scheitern.
|
|
*/
|
|
export async function sendChatNotification(customer, text) {
|
|
try {
|
|
const mailer = getTransporter()
|
|
if (!mailer || !config.chatNotify.emails.length) return { sent: false, reason: 'nicht konfiguriert' }
|
|
|
|
const now = Date.now()
|
|
const last = lastNotifyByCustomer.get(customer.$id) || 0
|
|
const throttleMs = config.chatNotify.throttleMin * 60 * 1000
|
|
if (now - last < throttleMs) return { sent: false, reason: 'throttled' }
|
|
lastNotifyByCustomer.set(customer.$id, now)
|
|
|
|
const mail = render({
|
|
template: 'T01',
|
|
values: { kunde_name: customer.name || customer.email || 'Kunde', nachricht: text },
|
|
})
|
|
await mailer.sendMail({
|
|
from: `"${TEAM_SENDER}" <${config.smtp.from}>`,
|
|
to: config.chatNotify.emails.join(', '),
|
|
subject: mail.subject,
|
|
text: mail.text,
|
|
html: mail.html,
|
|
})
|
|
return { sent: true }
|
|
} catch (err) {
|
|
console.error('[mailer] Chat-Benachrichtigung fehlgeschlagen:', err.message)
|
|
return { sent: false, reason: err.message }
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Wartungs-Abo: Vertrags-/Kuendigungs-/Widerrufsbestaetigungen
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/**
|
|
* Hinweis an das WEBklar-Team (Abo-Ereignisse) aus einer T-Vorlage; der Betreff steht in der Vorlage.
|
|
* Fehler werden nur geloggt.
|
|
* @param {{ template: string, values?: object, blocks?: object }} message
|
|
*/
|
|
export async function sendAdminNotice(message) {
|
|
try {
|
|
const mailer = getTransporter()
|
|
if (!mailer || !config.chatNotify.emails.length) return { sent: false, reason: 'nicht konfiguriert' }
|
|
const mail = render(message)
|
|
await mailer.sendMail({
|
|
from: `"${TEAM_SENDER}" <${config.smtp.from}>`,
|
|
to: config.chatNotify.emails.join(', '),
|
|
subject: mail.subject,
|
|
text: mail.text,
|
|
html: mail.html,
|
|
})
|
|
return { sent: true }
|
|
} catch (err) {
|
|
console.error('[mailer] Admin-Hinweis fehlgeschlagen:', err.message)
|
|
return { sent: false, reason: err.message }
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Mail an Kunden aus einer K-Vorlage (HTML mit Textfassung), Kopie per BCC ans Team.
|
|
* `attachments` fuer die Vertragsbestaetigung auf dauerhaftem Datentraeger (§ 312f BGB):
|
|
* Vertragsunterlagen als PDF.
|
|
* Ergebnis: nodemailer-Info (messageId, Antwort des Mailservers, abgelehnte Empfaenger).
|
|
* @param {string} to
|
|
* @param {{ template: string, values?: object, blocks?: object, attachments?: object[] }} message
|
|
*/
|
|
export async function sendCustomerMail(to, { attachments = [], ...message }) {
|
|
const mailer = getTransporter()
|
|
if (!mailer) throw new Error('SMTP nicht konfiguriert')
|
|
const mail = render(message)
|
|
return mailer.sendMail({
|
|
from: `"${CUSTOMER_SENDER}" <${config.smtp.from}>`,
|
|
replyTo: config.smtp.replyTo || undefined,
|
|
to,
|
|
bcc: config.chatNotify.emails.join(', ') || undefined,
|
|
subject: mail.subject,
|
|
text: mail.text,
|
|
html: mail.html,
|
|
attachments,
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Mail an eine einzelne Person im Team aus einer T-Vorlage (z. B. T15 Push-Zeitmail), ohne BCC.
|
|
* Wirft bei fehlendem SMTP oder Versandfehler - der Aufrufer entscheidet, ob das schlimm ist.
|
|
* @param {string} to
|
|
* @param {{ template: string, values?: object, blocks?: object }} message
|
|
*/
|
|
export async function sendStaffMail(to, message) {
|
|
const mailer = getTransporter()
|
|
if (!mailer) throw new Error('SMTP nicht konfiguriert')
|
|
const mail = render(message)
|
|
return mailer.sendMail({
|
|
from: `"${TEAM_SENDER}" <${config.smtp.from}>`,
|
|
to,
|
|
subject: mail.subject,
|
|
text: mail.text,
|
|
html: mail.html,
|
|
})
|
|
}
|