import { readSubscribers, writeSubscribers } from '../../utils/newsletter.js' import { randomUUID } from 'crypto' import nodemailer from 'nodemailer' import crypto from 'crypto' import fs from 'fs/promises' import path from 'path' const getDataPath = (filename) => { const cwd = process.cwd() if (cwd.endsWith('.output')) { return path.join(cwd, '../server/data', filename) } return path.join(cwd, 'server/data', filename) } const NEWSLETTER_GROUPS_FILE = getDataPath('newsletter-groups.json') async function readGroups() { try { const data = await fs.readFile(NEWSLETTER_GROUPS_FILE, 'utf-8') return JSON.parse(data) } catch (error) { if (error.code === 'ENOENT') { return [] } throw error } } export default defineEventHandler(async (event) => { try { const body = await readBody(event) const { email, name, groupId } = body if (!email || !email.match(/^[^\s@]+@[^\s@]+\.[^\s@]+$/)) { throw createError({ statusCode: 400, statusMessage: 'Ungültige E-Mail-Adresse' }) } if (!groupId) { throw createError({ statusCode: 400, statusMessage: 'Newsletter-Gruppe muss ausgewählt werden' }) } // Prüfe ob Gruppe existiert und für externe Abonnements verfügbar ist const groups = await readGroups() const group = groups.find(g => g.id === groupId) if (!group) { throw createError({ statusCode: 404, statusMessage: 'Newsletter-Gruppe nicht gefunden' }) } if (group.type !== 'subscription' || group.sendToExternal !== true) { throw createError({ statusCode: 403, statusMessage: 'Diese Newsletter-Gruppe ist nicht für externe Abonnements verfügbar' }) } const subscribers = await readSubscribers() const emailLower = email.toLowerCase() // Prüfe ob bereits für diese Gruppe angemeldet const existing = subscribers.find(s => { const sEmail = (s.email || '').toLowerCase() return sEmail === emailLower && s.groupIds && s.groupIds.includes(groupId) }) if (existing) { if (existing.confirmed) { throw createError({ statusCode: 409, statusMessage: 'Sie sind bereits für diesen Newsletter angemeldet' }) } else { // Bestätigungsmail erneut senden await sendConfirmationEmail(existing.email, existing.name || name, existing.confirmationToken, group.name) return { success: true, message: 'Eine Bestätigungsmail wurde an Ihre E-Mail-Adresse gesendet' } } } // Prüfe ob E-Mail bereits existiert (für andere Gruppe oder ohne Gruppe) const existingEmail = subscribers.find(s => (s.email || '').toLowerCase() === emailLower) if (existingEmail) { // Bestehender Subscriber - Gruppe hinzufügen if (!existingEmail.groupIds) { existingEmail.groupIds = [] } if (existingEmail.groupIds.includes(groupId)) { // Bereits für diese Gruppe angemeldet if (existingEmail.confirmed) { throw createError({ statusCode: 409, statusMessage: 'Sie sind bereits für diesen Newsletter angemeldet' }) } else { // Bestätigungsmail erneut senden await sendConfirmationEmail(existingEmail.email, existingEmail.name || name, existingEmail.confirmationToken, group.name) return { success: true, message: 'Eine Bestätigungsmail wurde an Ihre E-Mail-Adresse gesendet' } } } // Gruppe hinzufügen existingEmail.groupIds.push(groupId) if (!existingEmail.confirmed) { // Neuer Bestätigungstoken für alle Gruppen existingEmail.confirmationToken = crypto.randomBytes(32).toString('hex') } existingEmail.name = name || existingEmail.name || '' await writeSubscribers(subscribers) if (existingEmail.confirmed) { // Bereits bestätigt - sofort aktiviert return { success: true, message: `Sie wurden erfolgreich für den Newsletter "${group.name}" angemeldet` } } else { // Bestätigungsmail senden await sendConfirmationEmail(existingEmail.email, existingEmail.name, existingEmail.confirmationToken, group.name) return { success: true, message: 'Eine Bestätigungsmail wurde an Ihre E-Mail-Adresse gesendet. Bitte bestätigen Sie Ihre Anmeldung.' } } } // Neuer Abonnent const confirmationToken = crypto.randomBytes(32).toString('hex') const unsubscribeToken = crypto.randomBytes(32).toString('hex') const newSubscriber = { id: randomUUID(), email: emailLower, name: name || '', groupIds: [groupId], confirmed: false, confirmationToken, unsubscribeToken, subscribedAt: new Date().toISOString(), confirmedAt: null, unsubscribedAt: null } subscribers.push(newSubscriber) await writeSubscribers(subscribers) // Bestätigungsmail senden await sendConfirmationEmail(email, name, confirmationToken, group.name) return { success: true, message: 'Eine Bestätigungsmail wurde an Ihre E-Mail-Adresse gesendet. Bitte bestätigen Sie Ihre Anmeldung.' } } catch (error) { console.error('Fehler bei Newsletter-Anmeldung:', error) if (error.statusCode) { throw error } throw createError({ statusCode: 500, statusMessage: 'Fehler bei der Newsletter-Anmeldung' }) } }) async function sendConfirmationEmail(email, name, token, groupName) { const smtpUser = process.env.SMTP_USER const smtpPass = process.env.SMTP_PASS if (!smtpUser || !smtpPass) { console.warn('SMTP-Credentials fehlen! Bestätigungsmail kann nicht gesendet werden.') return } const baseUrl = process.env.NUXT_PUBLIC_BASE_URL || 'http://localhost:3100' const confirmationUrl = `${baseUrl}/newsletter/confirm?token=${token}` 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 } }) await transporter.sendMail({ from: process.env.SMTP_FROM || 'noreply@harheimertc.de', to: email, subject: `Newsletter-Anmeldung bestätigen - ${groupName} - Harheimer TC`, html: `
Hallo ${name || 'Liebe/r Abonnent/in'},
vielen Dank für Ihre Anmeldung zum Newsletter "${groupName}" des Harheimer TC!
Bitte bestätigen Sie Ihre Anmeldung, indem Sie auf den folgenden Link klicken:
Newsletter-Anmeldung bestätigen
Falls Sie sich nicht angemeldet haben, können Sie diese E-Mail ignorieren.
Mit sportlichen Grüßen,
Ihr Harheimer Tischtennis-Club 1954 e.V.