v1.22: Verwaltung sendet Direkt-E-Mails an Mieter mit CC an die Verwaltung

This commit is contained in:
2026-10-04 14:17:12 +02:00
parent 497abfcd4b
commit f15fc6ee0c
10 changed files with 410 additions and 8 deletions
+50
View File
@@ -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 {