/// // Alles, was in dieser Anwendung eine Mail verschickt, geht hier durch. // // Eigene Datei, weil PocketBase jeden Route- und Event-Handler in einer // eigenen JS-Laufzeit ausführt: Was neben routerAdd steht, sieht der Handler // nicht. Er holt sich seine Helfer per require(). // // Drei Dinge stehen hier, die sonst in jeder Mail neu erfunden würden: // woher der Name der App kommt, woher die Adresse des Frontends kommt, und // wie aus einer Vorlage samt Daten eine verschickte Mail wird. const DEFAULT_APP_NAME = 'Kurbeler' /** * Name der Anwendung, wie er in Mails auftaucht. * * Über `APP_NAME` überschreibbar — eine Testinstanz soll in der Betreffzeile * erkennbar sein, ohne dass jemand den Code anfasst. */ function appName() { return $os.getenv('APP_NAME') || DEFAULT_APP_NAME } /** * Adresse des Frontends, ohne Schrägstrich am Ende. * * Das ist NICHT `settings().meta.appURL`: Die zeigt auf PocketBase selbst, * und ein Einladungslink dorthin führte auf die API statt auf die Seite, die * das Konto anlegt. * * Und es ist ausdrücklich nicht der `Origin`-Header des Aufrufers. Der steht * unter der Kontrolle dessen, der die Anfrage stellt — ein Link in einer * Mail, den ein Fremder auf seinen eigenen Server zeigen lassen kann, ist * eine Phishing-Vorlage mit unserem Absender darauf. * * Fehlt die Variable, gibt es keine Mail. Lieber ein Fehler, den die * Teamleitung sieht, als ein Link ins Leere, den sie nie zu Gesicht bekommt. */ function appURL() { const raw = ($os.getenv('APP_URL') || '').trim() if (!raw) { return '' } return raw.replace(/\/+$/, '') } /** Eine Vorlage aus pb_hooks/views/ mit Daten füllen. */ function render(view, data) { return $template.loadFiles(`${__hooks}/views/${view}`).render(data) } /** * Absender aus den PocketBase-Einstellungen. Dort wird auch SMTP eingerichtet; * beides gehört ins Admin-UI und nicht in den Code. */ function sender(app) { const meta = app.settings().meta return { address: meta.senderAddress, name: meta.senderName || appName(), } } /** * Mail verschicken. Wirft weiter, wenn der Versand scheitert — ob das den * Aufrufer scheitern lässt, entscheidet er selbst: Eine Einladung ohne Mail * ist wertlos und muss scheitern, eine Benachrichtigung darf im Stillen * verloren gehen, statt das Speichern eines Events zu verhindern. */ function send(app, to, subject, html) { const message = new MailerMessage({ from: sender(app), to: [{ address: to.address, name: to.name || '' }], subject: subject, html: html, }) app.newMailClient().send(message) } /** Vorlage rendern und verschicken — der übliche Weg. */ function sendView(app, to, subject, view, data) { send(app, to, subject, render(view, data)) } /** * Ein Datum, wie es in einer Mail stehen soll: „30.09.2026". * * PocketBase liefert „2026-09-30 10:00:00.000Z"; das direkt hineinzuschreiben * sähe aus wie ein Datenbankfeld und nicht wie ein Satz. */ function formatDay(value) { const day = (value || '').slice(0, 10) if (day.length !== 10) { return '' } const parts = day.split('-') return `${parts[2]}.${parts[1]}.${parts[0]}` } module.exports = { appName: appName, appURL: appURL, render: render, send: send, sendView: sendView, formatDay: formatDay, }