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,127 @@
import multer from 'multer'
import fs from 'fs/promises'
import path from 'path'
import sharp from 'sharp'
import { getUserFromToken, verifyToken } from '../../utils/auth.js'
import { randomUUID } from 'crypto'
// 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')
// Multer-Konfiguration für Bild-Uploads
const storage = multer.diskStorage({
destination: async (req, file, cb) => {
try {
await fs.mkdir(PERSONEN_DIR, { recursive: true })
cb(null, PERSONEN_DIR)
} catch (error) {
cb(error)
}
},
filename: (req, file, cb) => {
const ext = path.extname(file.originalname)
const filename = `${randomUUID()}${ext}`
cb(null, filename)
}
})
const upload = multer({
storage,
fileFilter: (req, file, cb) => {
const allowedMimes = ['image/jpeg', 'image/jpg', 'image/png', 'image/gif', 'image/webp']
if (allowedMimes.includes(file.mimetype)) {
cb(null, true)
} else {
cb(new Error('Nur Bilddateien sind erlaubt (JPEG, PNG, GIF, WebP)'), false)
}
},
limits: {
fileSize: 10 * 1024 * 1024 // 10MB Limit
}
})
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 decoded = verifyToken(token)
if (!decoded) {
throw createError({
statusCode: 401,
statusMessage: 'Ungültiges Token'
})
}
const user = await getUserFromToken(token)
if (!user || (user.role !== 'admin' && user.role !== 'vorstand')) {
throw createError({
statusCode: 403,
statusMessage: 'Keine Berechtigung zum Hochladen von Bildern'
})
}
// Multer-Middleware für multipart/form-data
await new Promise((resolve, reject) => {
upload.single('image')(event.node.req, event.node.res, (err) => {
if (err) reject(err)
else resolve()
})
})
const file = event.node.req.file
if (!file) {
throw createError({
statusCode: 400,
statusMessage: 'Keine Bilddatei hochgeladen'
})
}
// Bild mit sharp verarbeiten (EXIF-Orientierung korrigieren und optional resize)
const originalPath = file.path
const ext = path.extname(file.originalname)
const newFilename = `${randomUUID()}${ext}`
const newPath = path.join(PERSONEN_DIR, newFilename)
// Bild verarbeiten: EXIF-Orientierung korrigieren
await sharp(originalPath)
.rotate()
.toFile(newPath)
// Temporäre Datei löschen
await fs.unlink(originalPath).catch(() => {})
return {
success: true,
message: 'Bild erfolgreich hochgeladen',
filename: newFilename
}
} catch (error) {
console.error('Fehler beim Hochladen des Personenfotos:', error)
if (error.statusCode) {
throw error
}
throw createError({
statusCode: 500,
statusMessage: error.message || 'Fehler beim Hochladen des Bildes'
})
}
})