131 lines
3.9 KiB
JavaScript
Executable File
131 lines
3.9 KiB
JavaScript
Executable File
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'
|
|
})
|
|
}
|
|
})
|