import nodemailer from 'nodemailer' import { promises as fs } from 'fs' import { createContactRequest } from '../utils/contact-requests.js' import { readUsers, migrateUserRoles, isHiddenUser } from '../utils/auth.js' import { sendNewContactRequestPush } from '../utils/push-notifications.js' // nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal // filename is always a hardcoded constant ('config.json'), never user input const getConfigPath = () => { const cwd = process.cwd() if (cwd.endsWith('.output')) return `${cwd}/../server/data/config.json` return `${cwd}/server/data/config.json` } async function loadConfig() { try { const configFile = getConfigPath() const raw = await fs.readFile(configFile, 'utf-8') return JSON.parse(raw) } catch (error) { console.error('Fehler beim Laden der Konfiguration für Kontaktanfragen:', error) return {} } } function envFlagEnabled(value) { return ['1', 'true', 'yes', 'on'].includes(String(value || '').trim().toLowerCase()) } function shouldUseDeveloperRecipients() { if (process.env.DEBUG !== undefined) return envFlagEnabled(process.env.DEBUG) return process.env.NODE_ENV !== 'production' || process.env.APP_ENV === 'test' } async function collectRecipients(config) { if (shouldUseDeveloperRecipients()) { return ['tsschulz@tsschulz.de'] } const recipients = [] // Vorstand: prefer active login users with the board role. try { const users = await readUsers() for (const rawUser of users) { if (!rawUser || rawUser.active === false || isHiddenUser(rawUser)) continue const user = migrateUserRoles({ ...rawUser }) const roles = Array.isArray(user.roles) ? user.roles : [] if (roles.includes('vorstand') && user.email && String(user.email).trim()) { recipients.push(String(user.email).trim()) } } } catch (error) { console.error('Fehler beim Laden der Vorstand-Empfänger aus Benutzerdaten:', error) } // Fallback: legacy config.json Vorstand object. if (recipients.length === 0 && config?.vorstand && typeof config.vorstand === 'object') { for (const member of Object.values(config.vorstand)) { if (member?.email && typeof member.email === 'string' && member.email.trim()) { recipients.push(member.email.trim()) } } } // Trainer if (Array.isArray(config?.trainer)) { for (const trainer of config.trainer) { if (trainer?.email && typeof trainer.email === 'string' && trainer.email.trim()) { recipients.push(trainer.email.trim()) } } } // Zusätzlich: Benutzer mit Trainer-Rolle aus dem Login-System try { const users = await readUsers() for (const rawUser of users) { if (isHiddenUser(rawUser)) continue const user = migrateUserRoles({ ...rawUser }) const roles = Array.isArray(user.roles) ? user.roles : [] if (roles.includes('trainer') && user.email && String(user.email).trim()) { recipients.push(String(user.email).trim()) } } } catch (error) { console.error('Fehler beim Laden der Trainer-Empfänger aus Benutzerdaten:', error) } const unique = [...new Set(recipients)] if (unique.length > 0) return unique // Fallback if (config?.website?.verantwortlicher?.email) { return [config.website.verantwortlicher.email] } throw new Error('Keine E-Mail-Empfänger in config.json konfiguriert.') } function createTransporter() { const smtpUser = process.env.SMTP_USER const smtpPass = process.env.SMTP_PASS || process.env.EMAIL_PASSWORD if (!smtpUser || !smtpPass) return null return nodemailer.createTransport({ host: process.env.SMTP_HOST || 'smtp.gmail.com', port: Number(process.env.SMTP_PORT || 587), secure: process.env.SMTP_SECURE === 'true', auth: { user: smtpUser, pass: smtpPass } }) } export default defineEventHandler(async (event) => { try { const body = await readBody(event) if (!body.name || !body.email || !body.subject || !body.message) { throw createError({ statusCode: 400, statusMessage: 'Alle Pflichtfelder müssen ausgefüllt werden' }) } const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/ if (!emailRegex.test(body.email)) { throw createError({ statusCode: 400, statusMessage: 'Ungültige E-Mail-Adresse' }) } // Anfrage immer speichern, auch wenn E-Mail-Versand fehlschlägt. const contactRequest = { name: String(body.name).trim(), email: String(body.email).trim(), phone: body.phone ? String(body.phone).trim() : '', subject: String(body.subject).trim(), message: String(body.message).trim() } await createContactRequest(contactRequest) sendNewContactRequestPush(contactRequest) .then(result => console.info('Kontaktanfrage-Push Ergebnis:', { subject: contactRequest.subject, ...result })) .catch(error => console.error('Kontaktanfrage-Push fehlgeschlagen:', error)) const config = await loadConfig() const recipients = await collectRecipients(config) const transporter = createTransporter() if (!transporter) { return { success: true, message: 'Anfrage wurde gespeichert. E-Mail-Versand ist aktuell nicht konfiguriert.' } } const nowLabel = new Date().toLocaleString('de-DE') const emailHtml = `
Name: ${body.name}
E-Mail: ${body.email}
Telefon: ${body.phone || 'Nicht angegeben'}
Betreff: ${body.subject}
${body.message}
Diese Nachricht wurde über das Kontaktformular der Harheimer TC Website gesendet.
Zeitstempel: ${nowLabel}