import fs from 'fs/promises' import path from 'path' import { getUserFromToken, hasAnyRole } from '../../utils/auth.js' import { randomUUID } from 'crypto' // nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal // filename is always a hardcoded constant (e.g., 'newsletter.json'), never user input 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 NEWSLETTERS_FILE = getDataPath('newsletters.json') async function readNewsletters() { try { const data = await fs.readFile(NEWSLETTERS_FILE, 'utf-8') return JSON.parse(data) } catch (error) { if (error.code === 'ENOENT') { return [] } throw error } } async function writeNewsletters(newsletters) { await fs.writeFile(NEWSLETTERS_FILE, JSON.stringify(newsletters, null, 2), 'utf-8') } export default defineEventHandler(async (event) => { try { // Authentifizierung prüfen const token = getCookie(event, 'auth_token') || getHeader(event, 'authorization')?.replace('Bearer ', '') if (!token) { throw createError({ statusCode: 401, statusMessage: 'Nicht authentifiziert' }) } const user = await getUserFromToken(token) if (!user || !hasAnyRole(user, 'admin', 'vorstand', 'newsletter')) { throw createError({ statusCode: 403, statusMessage: 'Keine Berechtigung' }) } const body = await readBody(event) const { title, content, type, targetGroup, sendToExternal } = body if (!title || !type) { throw createError({ statusCode: 400, statusMessage: 'Titel und Typ sind erforderlich' }) } if (type === 'subscription' && sendToExternal === undefined) { throw createError({ statusCode: 400, statusMessage: 'sendToExternal ist für Abonnenten-Newsletter erforderlich' }) } if (type === 'group' && !targetGroup) { throw createError({ statusCode: 400, statusMessage: 'Zielgruppe ist für Gruppen-Newsletter erforderlich' }) } const newsletters = await readNewsletters() const newNewsletter = { id: randomUUID(), title, content, type, // 'subscription' oder 'group' targetGroup: type === 'group' ? targetGroup : null, // 'alle', 'erwachsene', 'nachwuchs', 'mannschaftsspieler', 'vorstand' sendToExternal: type === 'subscription' ? sendToExternal : false, // true = auch extern, false = nur intern status: 'draft', // 'draft', 'sent' createdAt: new Date().toISOString(), createdBy: user.id, sentAt: null, sentTo: null } newsletters.push(newNewsletter) await writeNewsletters(newsletters) return { success: true, message: 'Newsletter erfolgreich erstellt', newsletter: { id: newNewsletter.id, title: newNewsletter.title, type: newNewsletter.type } } } catch (error) { console.error('Fehler beim Erstellen des Newsletters:', error) if (error.statusCode) { throw error } throw createError({ statusCode: 500, statusMessage: 'Fehler beim Erstellen des Newsletters' }) } })