import fs from 'fs/promises' import path from 'path' import { getUserFromToken, hasAnyRole } from '../../utils/auth.js' export default defineEventHandler(async (event) => { try { const token = getCookie(event, 'auth_token') const currentUser = token ? await getUserFromToken(token) : null if (!currentUser) { throw createError({ statusCode: 401, statusMessage: 'Nicht authentifiziert' }) } if (!hasAnyRole(currentUser, 'admin', 'vorstand')) { throw createError({ statusCode: 403, statusMessage: 'Keine Berechtigung' }) } const { filename, content } = await readBody(event) if (!filename || !content) { throw createError({ statusCode: 400, statusMessage: 'Filename und Content sind erforderlich' }) } // Sicherheitsprüfung: Nur bestimmte Dateien erlauben const allowedFiles = [ 'vereinsmeisterschaften.csv', 'mannschaften.csv', 'termine.csv', 'spielplan.csv' ] if (!allowedFiles.includes(filename)) { throw createError({ statusCode: 403, statusMessage: 'Datei nicht erlaubt' }) } // Wichtig: In Production werden statische Dateien aus `.output/public` ausgeliefert. // Wenn PM2 `cwd` auf das Repo-Root setzt, ist `process.cwd()` NICHT `.output` – // daher schreiben wir robust in alle sinnvollen Zielorte: // - `.output/public/data/` (damit die laufende Instanz sofort die neuen Daten liefert) // - `public/data/` (damit der nächste Build die Daten wieder übernimmt) // // nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal // filename is validated against allowlist above, path traversal prevented const cwd = process.cwd() const candidatePaths = [ path.join(cwd, '.output/public/data', filename), path.join(cwd, 'public/data', filename), path.join(cwd, '../public/data', filename), // falls cwd z.B. `.output` oder `.output/server` ist path.join(cwd, '../.output/public/data', filename) ] const uniquePaths = [...new Set(candidatePaths)] const writeResults = [] const writeErrors = [] for (const targetPath of uniquePaths) { try { const dataDir = path.dirname(targetPath) await fs.mkdir(dataDir, { recursive: true }) await fs.writeFile(targetPath, content, 'utf8') writeResults.push(targetPath) } catch (e) { writeErrors.push({ targetPath, error: e }) } } if (writeResults.length === 0) { console.error('Konnte CSV-Datei in keinen Zielpfad schreiben:', writeErrors) throw createError({ statusCode: 500, statusMessage: 'Fehler beim Speichern der Datei' }) } return { success: true, message: 'Datei erfolgreich gespeichert', writtenTo: writeResults } } catch (error) { console.error('Fehler beim Speichern der CSV-Datei:', error) if (error.statusCode) { throw error } throw createError({ statusCode: 500, statusMessage: 'Fehler beim Speichern der Datei' }) } })