Terminplaner: Kalenderdatei, Gaesteversand, Vorlagen-Engine fuer G-Vorlagen

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Sonnet 5.5 committed 2026-10-03 08:17:43 +00:00
1 parent 0c033ab1d2
commit 181445a69d
5 files changed
+514 -6

No files matched your search

+38
View File
@@ -176,6 +176,44 @@ export async function sendCustomerMail(to, { attachments = [], bccTeam = true, .
})
}
// Genau ein Postfach (Terminplaner: kein Mail-Relais ueber Listen oder Anzeigenamen), hoechstens 254 Zeichen
const SINGLE_MAILBOX = /^[^\s,;<>"\\]+@[^\s,;<>"\\]+$/
const checkMailbox = (value, name) => {
if (typeof value !== 'string' || value.length > 254 || !SINGLE_MAILBOX.test(value)) {
throw new TypeError(`${name}: genau eine E-Mail-Adresse erwartet`)
}
}
const oneLine = (value) => String(value ?? '').replace(/\s*[\r\n]+\s*/g, ' ').trim()
/**
* Terminplaner: Mail an einen Gast (Spec 2026-10-02 Abschnitt 6). Verschickt schon gerenderte Inhalte (Betreff, HTML, Text
* kommen aus termineOutbox/G-Vorlagen); Absendername = Betrieb, Adresse bleibt SMTP_FROM, Antworten gehen an `replyTo`
* (Betriebs-E-Mail), nie BCC. Mit `ics` haengt die Kalenderdatei als icalEvent (nodemailer-Kalenderteil, kein Anhang) an.
* Wirft bei fehlendem SMTP, ungueltiger Adresse oder Methode - der Aufrufer (Outbox) entscheidet ueber Wiederholung.
* @param {{ to: string, fromName: string, replyTo?: string|null, subject: string, html: string, text: string,
* ics?: string|null, icsMethod?: 'REQUEST'|'CANCEL'|'PUBLISH' }} message
*/
export async function sendGuestMail({ to, fromName, replyTo = null, subject, html, text, ics = null, icsMethod = 'REQUEST' }) {
const mailer = getTransporter()
if (!mailer) throw new Error('SMTP nicht konfiguriert')
checkMailbox(to, 'to')
if (replyTo) checkMailbox(replyTo, 'replyTo')
const name = oneLine(fromName)
const subj = oneLine(subject)
if (!name) throw new TypeError('fromName fehlt')
if (!subj) throw new TypeError('subject fehlt')
if (ics && !['REQUEST', 'CANCEL', 'PUBLISH'].includes(icsMethod)) throw new TypeError('icsMethod muss REQUEST, CANCEL oder PUBLISH sein')
return mailer.sendMail({
from: { name, address: config.smtp.from },
replyTo: replyTo || undefined,
to,
subject: subj,
text,
html,
icalEvent: ics ? { method: icsMethod, filename: 'termin.ics', content: ics } : undefined,
})
}
/**
* 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.