From 3d3e22bb1bb6be1d1645a68407699ffafb0df403 Mon Sep 17 00:00:00 2001 From: "Torsten Schulz (local)" Date: Wed, 11 Feb 2026 15:41:03 +0100 Subject: [PATCH] =?UTF-8?q?Implementiere=20zentralen=20E-Mail-Service=20f?= =?UTF-8?q?=C3=BCr=20Registrierungsbenachrichtigungen=20und=20entferne=20v?= =?UTF-8?q?eralteten=20Code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/api/auth/register.post.js | 58 +------------- server/api/membership/generate-pdf.post.js | 67 ++-------------- server/utils/email-service.js | 91 ++++++++++++++++++---- 3 files changed, 88 insertions(+), 128 deletions(-) diff --git a/server/api/auth/register.post.js b/server/api/auth/register.post.js index 6a90b58..d15239c 100644 --- a/server/api/auth/register.post.js +++ b/server/api/auth/register.post.js @@ -1,5 +1,5 @@ import { readUsers, writeUsers, hashPassword } from '../../utils/auth.js' -import nodemailer from 'nodemailer' +import { sendRegistrationNotification } from '../../utils/email-service.js' import { assertPasswordNotPwned } from '../../utils/hibp.js' export default defineEventHandler(async (event) => { @@ -55,61 +55,11 @@ export default defineEventHandler(async (event) => { users.push(newUser) await writeUsers(users) - // Send notification email to admin + // Send notification to Vorstand/admin via central email service try { - const smtpUser = process.env.SMTP_USER - const smtpPass = process.env.SMTP_PASS - - if (!smtpUser || !smtpPass) { - console.warn('SMTP-Credentials fehlen! E-Mail-Versand wird übersprungen.') - console.warn(`SMTP_USER=${smtpUser ? 'gesetzt' : 'FEHLT'}, SMTP_PASS=${smtpPass ? 'gesetzt' : 'FEHLT'}`) - // Continue without sending email - } else { - const transporter = nodemailer.createTransport({ - host: process.env.SMTP_HOST || 'smtp.gmail.com', - port: process.env.SMTP_PORT || 587, - secure: false, - auth: { - user: smtpUser, - pass: smtpPass - } - }) - - // Email to admin - await transporter.sendMail({ - from: process.env.SMTP_FROM || 'noreply@harheimertc.de', - to: process.env.SMTP_ADMIN || 'j.dichmann@gmx.de', - subject: 'Neue Registrierung - Harheimer TC', - html: ` -

Neue Registrierung

-

Ein neuer Benutzer hat sich registriert und wartet auf Freigabe:

- -

Bitte prüfen Sie die Registrierung im CMS.

- ` - }) - - // Email to user - await transporter.sendMail({ - from: process.env.SMTP_FROM || 'noreply@harheimertc.de', - to: email, - subject: 'Registrierung erhalten - Harheimer TC', - html: ` -

Registrierung erhalten

-

Hallo ${name},

-

vielen Dank für Ihre Registrierung beim Harheimer TC!

-

Ihre Anfrage wird vom Vorstand geprüft. Sie erhalten eine E-Mail, sobald Ihr Zugang freigeschaltet wurde.

-
-

Mit sportlichen Grüßen,
Ihr Harheimer TC

- ` - }) - } + await sendRegistrationNotification({ name, email, phone }) } catch (emailError) { - console.error('E-Mail-Versand fehlgeschlagen:', emailError) - // Continue anyway - user is registered + console.error('Registrierungs-Benachrichtigung fehlgeschlagen:', emailError) } return { diff --git a/server/api/membership/generate-pdf.post.js b/server/api/membership/generate-pdf.post.js index 9bae67d..dbd1282 100644 --- a/server/api/membership/generate-pdf.post.js +++ b/server/api/membership/generate-pdf.post.js @@ -5,6 +5,7 @@ import fs from 'fs/promises' import path from 'path' import { StandardFonts } from 'pdf-lib' import { getDownloadCookieOptionsWithMaxAge } from '../../utils/cookies.js' +import { sendMembershipEmail as sendMembershipEmailUtil } from '../../utils/email-service.js' // const require = createRequire(import.meta.url) // Nicht verwendet const execAsync = promisify(exec) @@ -317,59 +318,7 @@ function getDataPath(filename) { return path.join(projectRoot, 'server', 'data', filename) } -async function sendMembershipEmail(data, _filename, _event) { - try { - const configPath = getDataPath('config.json') - const configData = await fs.readFile(configPath, 'utf8') - const config = JSON.parse(configData) - - let recipients = [] - let subject = `Neuer Mitgliedschaftsantrag - ${data.vorname} ${data.nachname}` - - // Sammle alle verfügbaren E-Mail-Adressen - const availableEmails = [] - - // Vorsitzender E-Mail hinzufügen (falls vorhanden) - if (config.vorstand.vorsitzender.email && config.vorstand.vorsitzender.email.trim() !== '') { - availableEmails.push(config.vorstand.vorsitzender.email) - } - - // Schriftführer E-Mail hinzufügen (falls vorhanden) - if (config.vorstand.schriftfuehrer.email && config.vorstand.schriftfuehrer.email.trim() !== '') { - availableEmails.push(config.vorstand.schriftfuehrer.email) - } - - // Fallback: Wenn keine E-Mails verfügbar sind, verwende tsschulz@tsschulz.de - if (availableEmails.length === 0) { - recipients = ['tsschulz@tsschulz.de'] - } else { - recipients = availableEmails - } - - // In nicht-Produktionsumgebung: Alle E-Mails an tsschulz@tsschulz.de - if (process.env.NODE_ENV !== 'production') { - recipients = ['tsschulz@tsschulz.de'] - } - - const message = `Ein neuer Mitgliedschaftsantrag wurde eingereicht. - -Antragsteller: ${data.vorname} ${data.nachname} -Mitgliedschaftsart: ${data.mitgliedschaftsart} -Volljährig: ${data.isVolljaehrig ? 'Ja' : 'Nein'} - -Das ausgefüllte Formular ist als Anhang verfügbar.` - - // E-Mail-Versand implementieren (hier würde normalerweise nodemailer verwendet) - console.log('E-Mail würde gesendet werden an:', recipients) - console.log('Betreff:', subject) - console.log('Nachricht:', message) - - return { success: true, recipients, subject, message } - } catch (error) { - console.error('Fehler beim Senden der E-Mail:', error) - return { success: false, error: error.message } - } -} +// Use central email service export default defineEventHandler(async (event) => { try { @@ -678,8 +627,8 @@ export default defineEventHandler(async (event) => { let emailResult if (usedTemplate) { - // E-Mail senden - emailResult = await sendMembershipEmail(data, filename, event) + // E-Mail senden via zentralen Service (pass full path) + emailResult = await sendMembershipEmailUtil(data, finalPdfPath) // Antragsdaten verschlüsselt speichern const encryptionKey = process.env.ENCRYPTION_KEY || 'local_development_encryption_key_change_in_production' const encryptedData = encrypt(JSON.stringify(data), encryptionKey) @@ -731,8 +680,8 @@ export default defineEventHandler(async (event) => { const finalPdfPath = path.join(uploadsDir, `${filename}.pdf`) await fs.copyFile(pdfPath, finalPdfPath) - // E-Mail senden - emailResult = await sendMembershipEmail(data, filename, event) + // E-Mail senden via zentralen Service (pass full path) + emailResult = await sendMembershipEmailUtil(data, finalPdfPath) // Antragsdaten verschlüsselt speichern const encryptionKey = process.env.ENCRYPTION_KEY || 'local_development_encryption_key_change_in_production' @@ -763,8 +712,8 @@ export default defineEventHandler(async (event) => { // Fallback: Einfache Textdatei generieren const fallbackFilename = await generateSimplePDF(data, filename, event) - // E-Mail senden (Fallback) - const emailResult = await sendMembershipEmail(data, filename, event) + // E-Mail senden (Fallback) + const emailResult = await sendMembershipEmailUtil(data, path.join(uploadsDir, `${filename}.txt`)) console.log('LaTeX nicht verfügbar, verwende Fallback-Lösung') console.log('E-Mail würde gesendet werden an:', emailResult.recipients || []) diff --git a/server/utils/email-service.js b/server/utils/email-service.js index 368b4f9..bbfa745 100644 --- a/server/utils/email-service.js +++ b/server/utils/email-service.js @@ -55,25 +55,29 @@ function getEmailRecipients(data, config) { } const recipients = [] - - // Add 1. Vorsitzender - if (config.vorsitzender && config.vorsitzender.email) { - recipients.push(config.vorsitzender.email) + + // Config uses a 'vorstand' object with nested roles; collect all emails + if (config.vorstand && typeof config.vorstand === 'object') { + Object.values(config.vorstand).forEach((member) => { + if (member && member.email && typeof member.email === 'string' && member.email.trim() !== '') { + recipients.push(member.email.trim()) + } + }) } - - // Add Schriftführer - if (config.schriftfuehrer && config.schriftfuehrer.email) { - recipients.push(config.schriftfuehrer.email) - } - - // For minors, also add 1. Trainer - if (!data.isVolljaehrig && config.trainer && config.trainer.email) { - recipients.push(config.trainer.email) + + // For minors, also add first trainer email if configured (trainer is an array) + if (!data.isVolljaehrig && Array.isArray(config.trainer) && config.trainer.length > 0 && config.trainer[0].email) { + recipients.push(config.trainer[0].email) } // Fallback if no recipients found if (recipients.length === 0) { - recipients.push('tsschulz@tsschulz.de') + // Prefer website verantwortlicher if set + if (config.website && config.website.verantwortlicher && config.website.verantwortlicher.email) { + recipients.push(config.website.verantwortlicher.email) + } else { + recipients.push('tsschulz@tsschulz.de') + } } return recipients @@ -94,7 +98,7 @@ function createTransporter() { ) } - return nodemailer.createTransporter({ + return nodemailer.createTransport({ host: process.env.SMTP_HOST || 'localhost', port: parseInt(process.env.SMTP_PORT) || 587, secure: process.env.SMTP_SECURE === 'true', @@ -161,4 +165,61 @@ Das ausgefüllte Formular ist als Anhang verfügbar.` error: error.message } } +} + +/** + * Sends a simple registration notification to Vorstand/admin and a confirmation to user. + * @param {Object} data - { name, email, phone } + */ +export async function sendRegistrationNotification(data) { + try { + const config = await loadConfig() + const recipients = getEmailRecipients(data, config) + + // Create transporter + const transporter = createTransporter() + + // Notify Vorstand/admin + const adminSubject = 'Neue Registrierung - Harheimer TC' + const adminHtml = ` +

Neue Registrierung

+

Ein neuer Benutzer hat sich registriert und wartet auf Freigabe:

+ +

Bitte prüfen Sie die Registrierung im CMS.

+ ` + + await transporter.sendMail({ + from: process.env.SMTP_FROM || 'noreply@harheimertc.de', + to: recipients.join(', '), + subject: adminSubject, + html: adminHtml + }) + + // Confirmation to user + const userSubject = 'Registrierung erhalten - Harheimer TC' + const userHtml = ` +

Registrierung erhalten

+

Hallo ${data.name},

+

vielen Dank für Ihre Registrierung beim Harheimer TC!

+

Ihre Anfrage wird vom Vorstand geprüft. Sie erhalten eine E-Mail, sobald Ihr Zugang freigeschaltet wurde.

+
+

Mit sportlichen Grüßen,
Ihr Harheimer TC

+ ` + + await transporter.sendMail({ + from: process.env.SMTP_FROM || 'noreply@harheimertc.de', + to: data.email, + subject: userSubject, + html: userHtml + }) + + return { success: true, recipients } + } catch (error) { + console.error('sendRegistrationNotification failed:', error.message || error) + throw error + } } \ No newline at end of file