import { promises as fs } from 'fs' import path from 'path' async function exists(filePath) { try { await fs.access(filePath) return true } catch { return false } } function parseCsvLine(line) { const values = [] let value = '' let quoted = false for (let index = 0; index < line.length; index++) { const char = line[index] if (char === '"') { if (quoted && line[index + 1] === '"') { value += '"' index++ } else { quoted = !quoted } } else if (char === ',' && !quoted) { values.push(value.trim()) value = '' } else { value += char } } values.push(value.trim()) return values } function categoryOrder(category) { const normalized = category.toLocaleLowerCase('de-DE') if (normalized.includes('einzel')) return 0 if (normalized.includes('doppel')) return 1 return 2 } function placementOrder(placement) { const match = String(placement).match(/\d+/) return match ? Number(match[0]) : Number.MAX_SAFE_INTEGER } function sortChampionshipCsv(csv) { const newline = csv.includes('\r\n') ? '\r\n' : '\n' const lines = csv.split(/\r?\n/) const [header, ...dataLines] = lines const entries = dataLines .filter(line => line.trim() !== '') .map((line, index) => ({ line, values: parseCsvLine(line), index })) entries.sort((a, b) => { const yearA = Number(a.values[0]) const yearB = Number(b.values[0]) if (Number.isFinite(yearA) && Number.isFinite(yearB) && yearA !== yearB) return yearB - yearA const categoryA = a.values[1] || '' const categoryB = b.values[1] || '' const categoryDifference = categoryOrder(categoryA) - categoryOrder(categoryB) if (categoryDifference !== 0) return categoryDifference const categoryNameDifference = categoryA.localeCompare(categoryB, 'de-DE') if (categoryNameDifference !== 0) return categoryNameDifference const placementDifference = placementOrder(a.values[2]) - placementOrder(b.values[2]) if (placementDifference !== 0) return placementDifference return a.index - b.index }) return [header, ...entries.map(entry => entry.line)].join(newline) } export default defineEventHandler(async (event) => { try { const cwd = process.cwd() // Prefer CMS write target first (server/data/public-data), then legacy locations. const candidates = [ path.join(cwd, 'server/data/public-data/vereinsmeisterschaften.csv'), path.join(cwd, '../server/data/public-data/vereinsmeisterschaften.csv'), path.join(cwd, '.output/server/data/vereinsmeisterschaften.csv'), path.join(cwd, 'server/data/vereinsmeisterschaften.csv'), path.join(cwd, '.output/public/data/vereinsmeisterschaften.csv'), path.join(cwd, 'public/data/vereinsmeisterschaften.csv'), path.join(cwd, '../.output/public/data/vereinsmeisterschaften.csv'), path.join(cwd, '../public/data/vereinsmeisterschaften.csv') ] let csvPath = null for (const candidate of candidates) { if (await exists(candidate)) { csvPath = candidate break } } if (!csvPath) { throw createError({ statusCode: 404, statusMessage: 'Vereinsmeisterschaften-Datei nicht gefunden' }) } // CSV-Datei direkt als Text zurückgeben (keine Caching-Probleme) const csv = await fs.readFile(csvPath, 'utf-8') // Setze Content-Type und Cache-Control Header setHeader(event, 'Content-Type', 'text/csv; charset=utf-8') setHeader(event, 'Cache-Control', 'no-cache, no-store, must-revalidate') setHeader(event, 'Pragma', 'no-cache') setHeader(event, 'Expires', '0') return sortChampionshipCsv(csv) } catch (error) { if (error?.statusCode) throw error console.error('Fehler beim Laden der Vereinsmeisterschaften:', error) throw createError({ statusCode: 500, statusMessage: 'Fehler beim Laden der Vereinsmeisterschaften' }) } })