Enhance Vereinsmeisterschaften and Vorstand pages with image support for players and board members. Implement lightbox functionality for player images in Vereinsmeisterschaften. Update CSV handling to include image filenames for better data management. Refactor components to utilize PersonCard for board members, improving code readability and maintainability.

This commit is contained in:
Torsten Schulz (local)
2025-12-18 13:37:03 +01:00
parent a004ffba9b
commit baf6c59c0d
14 changed files with 756 additions and 186 deletions

View File

@@ -0,0 +1,108 @@
import fs from 'fs/promises'
import path from 'path'
import sharp from 'sharp'
// Handle both dev and production paths
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 PERSONEN_DIR = getDataPath('personen')
export default defineEventHandler(async (event) => {
try {
const filename = getRouterParam(event, 'filename')
if (!filename) {
throw createError({
statusCode: 400,
statusMessage: 'Dateiname erforderlich'
})
}
// Sicherheitsprüfung: Nur erlaubte Dateinamen (UUID-Format)
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.(jpg|jpeg|png|gif|webp)$/i.test(filename)) {
throw createError({
statusCode: 400,
statusMessage: 'Ungültiger Dateiname'
})
}
const filePath = path.join(PERSONEN_DIR, filename)
// Prüfe ob Datei existiert
try {
await fs.access(filePath)
} catch {
throw createError({
statusCode: 404,
statusMessage: 'Bild nicht gefunden'
})
}
// MIME-Type bestimmen
const ext = path.extname(filename).toLowerCase()
const mimeTypes = {
'.jpg': 'image/jpeg',
'.jpeg': 'image/jpeg',
'.png': 'image/png',
'.gif': 'image/gif',
'.webp': 'image/webp'
}
const contentType = mimeTypes[ext] || 'application/octet-stream'
// Optional: Query-Parameter für Größe
const query = getQuery(event)
const width = query.width ? parseInt(query.width) : null
const height = query.height ? parseInt(query.height) : null
let imageBuffer = await fs.readFile(filePath)
// Bild verarbeiten falls Größe angegeben
if (width || height) {
const resizeOptions = {}
if (width && height) {
resizeOptions.width = width
resizeOptions.height = height
resizeOptions.fit = 'cover'
} else if (width) {
resizeOptions.width = width
resizeOptions.fit = 'inside'
resizeOptions.withoutEnlargement = true
} else if (height) {
resizeOptions.height = height
resizeOptions.fit = 'inside'
resizeOptions.withoutEnlargement = true
}
imageBuffer = await sharp(imageBuffer)
.rotate() // EXIF-Orientierung korrigieren
.resize(resizeOptions)
.toBuffer()
} else {
// Nur EXIF-Orientierung korrigieren
imageBuffer = await sharp(imageBuffer).rotate().toBuffer()
}
setHeader(event, 'Content-Type', contentType)
setHeader(event, 'Cache-Control', 'public, max-age=31536000')
return imageBuffer
} catch (error) {
console.error('Fehler beim Laden des Personenfotos:', error)
if (error.statusCode) {
throw error
}
throw createError({
statusCode: 500,
statusMessage: 'Fehler beim Laden des Bildes'
})
}
})