/** Serverseitiger E-Mail-Versand über die Portal-Mail-API (mp-mail-api). * * Fire-and-forget: Ein Fehler (Mail-API down, Relay-Fehler) beeinträchtigt * die auslösende Portal-Aktion nie. Die Mail-API selbst puffert im Fehlerfall * in ihrer Outbox und versucht es erneut. * * Gestaltung wie im Schulportal: HTML-Karte (600 px, Kopfzeile in Portal-Farben, * Zeilen-Tabelle, optional Status-Box/Zitat/Aktions-Button) plus Plain-Text- * Fallback, immer beide an die Mail-API (sendet multipart/alternative). */ const MAIL_API_URL = process.env.MAIL_API_URL || '' // z. B. http://mp-mail-api:8080 const MAIL_API_TOKEN = process.env.MAIL_API_TOKEN || '' const PORTAL_URL = process.env.PORTAL_URL || 'https://mieterportal.hocks.eu' // Portal-Farben (Hinterstein): dunkles Petrol + Amber-Akzent (vgl. Login-Seite) const FARBE_KOPF = '#22333b' // Dunkel-Petrol const FARBE_KOPF_ZEILE = '#fcd34d' // Amber-200 (Kicker-Zeile) const FARBE_UNTERTITEL = '#cbd5e1' const FARBE_BUTTON = '#f59e0b' // Amber-500 const FARBE_BUTTON_TEXT = '#1c1917' const HINTERGRUND = '#f1f5f9' const KARTE = '#ffffff' const TEXT = '#0f172a' const TEXT_SOFT = '#64748b' const RAND = '#e2e8f0' const FUSSZEILE_TEXT = '— Automatische Benachrichtigung aus dem Mieterportal der Hinterstein Immobilien' function htmlEscape(s) { return String(s) .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') } /** Baut die HTML-Karte im Schulportal-Stil. Alle Inhalte werden escaped. */ function baueHtmlMail(opts) { const { titel, untertitel = '', einleitung = '', zeilen = [], statusBox, zitat, aktionenHtml = '', fussnote = '' } = opts const zeilenHtml = zeilen .map( ([label, wert]) => ` ${htmlEscape(label)} ${htmlEscape(wert)} `, ) .join('') const statusHtml = statusBox ? `
${htmlEscape(statusBox.text)}
` : '' const zitatHtml = zitat ? `
${htmlEscape(zitat)}
` : '' const fussnotenHtml = fussnote ? `

${htmlEscape(fussnote)}

` : '' return ` ${htmlEscape(titel)}
${htmlEscape(einleitung || titel)}

Hinterstein Immobilien

${htmlEscape(titel)}

${untertitel ? `

${htmlEscape(untertitel)}

` : ''}
${einleitung ? `

${htmlEscape(einleitung)}

` : ''} ${statusHtml} ${zitatHtml} ${zeilenHtml}
${aktionenHtml} ${fussnotenHtml}

Hinterstein Immobilien (Demo) · mieterportal.hocks.eu
Diese E-Mail wurde automatisch vom Mieterportal erzeugt. Bitte antworten Sie nicht direkt – nutzen Sie das Nachrichten-Modul im Portal.

` } /** Aktions-Button (portal-interne Links). */ function buttonHtml(text, url) { return `

${htmlEscape(text)}

` } async function sendeMail(an, betreff, text, html, cc) { if (!MAIL_API_URL || !MAIL_API_TOKEN || !an) return try { const res = await fetch(`${MAIL_API_URL}/send`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Api-Token': MAIL_API_TOKEN }, body: JSON.stringify({ to: an, subject: betreff, text: `${text}\n\n${FUSSZEILE_TEXT}`, ...(html ? { html } : {}), ...(cc ? { cc } : {}), // v1.22: Komma-getrennte CC-Empfänger }), }) if (!res.ok) console.warn(`[mail] Versand an ${an} fehlgeschlagen: HTTP ${res.status}`) } catch (e) { console.warn(`[mail] Versand an ${an} fehlgeschlagen:`, e.message) } } /** E-Mail-Adressen aller aktiven Verwaltungsnutzer (komma-getrennt, für CC). */ async function verwaltungEmails() { const { rows } = await require('./db').pool.query( `SELECT email FROM users WHERE rolle = 'verwaltung' AND aktiv AND email <> ''`, ) return rows.map((r) => r.email).join(', ') } /** Alle aktiven Verwaltungsnutzer benachrichtigen. */ async function benachrichtigeVerwaltung(betreff, text, html) { const { rows } = await require('./db').pool.query( `SELECT email FROM users WHERE rolle = 'verwaltung' AND aktiv AND email <> ''`, ) for (const r of rows) sendeMail(r.email, betreff, text, html) } /** Mieter einer bestimmten Wohnung benachrichtigen. */ async function benachrichtigeMieter(einheitId, betreff, text, html) { const { rows } = await require('./db').pool.query( `SELECT email FROM users WHERE rolle = 'mieter' AND aktiv AND einheit_id = $1 AND email <> ''`, [einheitId], ) for (const r of rows) sendeMail(r.email, betreff, text, html) } function einheitLabel(u) { return u ? `${u.building}, Whg. ${u.number}` : '' } module.exports = { sendeMail, benachrichtigeVerwaltung, benachrichtigeMieter, verwaltungEmails, einheitLabel, baueHtmlMail, buttonHtml, }