v1.22: Verwaltung sendet Direkt-E-Mails an Mieter mit CC an die Verwaltung
This commit is contained in:
@@ -1238,11 +1238,61 @@ app.patch('/api/users/:id', auth, adminOnly, async (req, res, next) => {
|
||||
await pool.query('UPDATE users SET aktiv = $1 WHERE id = $2', [Boolean(b.aktiv), req.params.id])
|
||||
if (b.einheitId !== undefined)
|
||||
await pool.query('UPDATE users SET einheit_id = $1 WHERE id = $2', [b.einheitId || null, req.params.id])
|
||||
if (b.email !== undefined) {
|
||||
const email = String(b.email || '').trim()
|
||||
if (email && !/^\S+@\S+\.\S+$/.test(email))
|
||||
return res.status(400).json({ fehler: 'Ungültige E-Mail-Adresse.' })
|
||||
if (email) {
|
||||
const { rows: fremde } = await pool.query(
|
||||
'SELECT id FROM users WHERE lower(email) = lower($1) AND id <> $2', [email, req.params.id],
|
||||
)
|
||||
if (fremde.length)
|
||||
return res.status(400).json({ fehler: 'Diese E-Mail-Adresse wird bereits verwendet.' })
|
||||
}
|
||||
await pool.query('UPDATE users SET email = $1 WHERE id = $2', [email, req.params.id])
|
||||
}
|
||||
const { rows: neu } = await pool.query('SELECT * FROM users WHERE id = $1', [req.params.id])
|
||||
res.json({ benutzer: oeffentlicherBenutzer(neu[0]) })
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
// Direkte E-Mail der Verwaltung an einen Mieter (immer mit CC an die
|
||||
// Verwaltung, damit die Kommunikation dort vollständig nachvollziehbar ist)
|
||||
app.post('/api/users/:id/email', auth, adminOnly, async (req, res, next) => {
|
||||
try {
|
||||
const betreff = String((req.body || {}).betreff || '').trim()
|
||||
const text = String((req.body || {}).text || '').trim()
|
||||
if (!betreff || !text)
|
||||
return res.status(400).json({ fehler: 'Betreff und Nachricht erforderlich.' })
|
||||
const { rows } = await pool.query(
|
||||
`SELECT * FROM users WHERE id = $1 AND rolle = 'mieter'`, [req.params.id],
|
||||
)
|
||||
const mieter = rows[0]
|
||||
if (!mieter) return res.status(404).json({ fehler: 'Mieter nicht gefunden.' })
|
||||
if (!mieter.email)
|
||||
return res.status(400).json({ fehler: `${mieter.name} hat keine E-Mail-Adresse hinterlegt (Profil).` })
|
||||
const { rows: einheitRows } = await pool.query('SELECT * FROM units WHERE id = $1', [mieter.einheit_id])
|
||||
const label = mail.einheitLabel(mapUnit(einheitRows[0]))
|
||||
const html = mail.baueHtmlMail({
|
||||
titel: 'Nachricht der Verwaltung',
|
||||
untertitel: label,
|
||||
einleitung: `Persönliche Nachricht von ${req.user.name} (Hinterstein Immobilien):`,
|
||||
zeilen: [
|
||||
['Wohnung', label],
|
||||
['Mieter', mieter.name],
|
||||
],
|
||||
zitat: text,
|
||||
aktionenHtml: mail.buttonHtml('Zum Mieterportal', `${process.env.PORTAL_URL || 'https://mieterportal.hocks.eu'}/nachrichten`),
|
||||
})
|
||||
const cc = await mail.verwaltungEmails()
|
||||
const textVersion =
|
||||
`Nachricht der Verwaltung an ${mieter.name} (${label}):\n\n„${text}“\n\n` +
|
||||
`— Gesendet von ${req.user.name} über das Mieterportal`
|
||||
mail.sendeMail(mieter.email, betreff, textVersion, html, cc)
|
||||
res.json({ ok: true })
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
// ── Registrierungscodes (nur Verwaltung) ─────────────────────
|
||||
app.get('/api/codes', auth, adminOnly, async (_req, res, next) => {
|
||||
try {
|
||||
|
||||
+15
-2
@@ -108,13 +108,17 @@ function buttonHtml(text, url) {
|
||||
return `<p style="margin:24px 0 0;"><a href="${htmlEscape(url)}" style="display:inline-block;background:${FARBE_BUTTON};color:${FARBE_BUTTON_TEXT};text-decoration:none;font-size:14px;font-weight:700;padding:12px 22px;border-radius:10px;">${htmlEscape(text)}</a></p>`
|
||||
}
|
||||
|
||||
async function sendeMail(an, betreff, text, html) {
|
||||
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 } : {}) }),
|
||||
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) {
|
||||
@@ -122,6 +126,14 @@ async function sendeMail(an, betreff, text, html) {
|
||||
}
|
||||
}
|
||||
|
||||
/** 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(
|
||||
@@ -147,6 +159,7 @@ module.exports = {
|
||||
sendeMail,
|
||||
benachrichtigeVerwaltung,
|
||||
benachrichtigeMieter,
|
||||
verwaltungEmails,
|
||||
einheitLabel,
|
||||
baueHtmlMail,
|
||||
buttonHtml,
|
||||
|
||||
Reference in New Issue
Block a user