Merge pull request 'Aktualisiere die Version auf 1.4.3, füge Validierung für Saison-Slugs hinzu und implementiere ein Logging-System für Fehler und Informationen' (#18) from dev into main
Reviewed-on: #18
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "harheimertc-website",
|
"name": "harheimertc-website",
|
||||||
"version": "1.4.2",
|
"version": "1.4.3",
|
||||||
"description": "Moderne Webseite für den Harheimer Tischtennis Club",
|
"description": "Moderne Webseite für den Harheimer Tischtennis Club",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -1,8 +1,18 @@
|
|||||||
import { listSpielplanSeasons, readSpielplanData } from '../utils/spielplan-data.js'
|
import { listSpielplanSeasons, readSpielplanData, validateSeasonSlug } from '../utils/spielplan-data.js'
|
||||||
|
import { error as loggerError } from '../utils/logger.js'
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
try {
|
try {
|
||||||
const query = getQuery(event)
|
const query = getQuery(event)
|
||||||
|
if (query.season && !validateSeasonSlug(query.season)) {
|
||||||
|
loggerError('Ungueltiger Saison-Slug angefragt', { season: query.season })
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: 'Ungueltiger Saison-Slug',
|
||||||
|
data: [],
|
||||||
|
headers: []
|
||||||
|
}
|
||||||
|
}
|
||||||
const [spielplan, seasons] = await Promise.all([
|
const [spielplan, seasons] = await Promise.all([
|
||||||
readSpielplanData({ season: query.season }),
|
readSpielplanData({ season: query.season }),
|
||||||
listSpielplanSeasons()
|
listSpielplanSeasons()
|
||||||
@@ -28,7 +38,7 @@ export default defineEventHandler(async (event) => {
|
|||||||
seasons
|
seasons
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Fehler beim Laden des Spielplans:', error)
|
loggerError('Fehler beim Laden des Spielplans:', { error })
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
message: 'Fehler beim Laden des Spielplans',
|
message: 'Fehler beim Laden des Spielplans',
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
import fs from 'fs/promises'
|
import fs from 'fs/promises'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import { readSpielplanData } from '../../utils/spielplan-data.js'
|
import { readSpielplanData, validateSeasonSlug } from '../../utils/spielplan-data.js'
|
||||||
|
import { info as loggerInfo, error as loggerError } from '../../utils/logger.js'
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
try {
|
try {
|
||||||
const query = getQuery(event)
|
const query = getQuery(event)
|
||||||
const team = query.team
|
const team = query.team
|
||||||
|
if (query.season && !validateSeasonSlug(query.season)) {
|
||||||
|
loggerError('Ungueltiger Saison-Slug angefragt (PDF)', { season: query.season })
|
||||||
|
throw createError({ statusCode: 400, statusMessage: 'Ungueltiger Saison-Slug' })
|
||||||
|
}
|
||||||
|
|
||||||
if (!team) {
|
if (!team) {
|
||||||
throw createError({
|
throw createError({
|
||||||
@@ -139,7 +144,7 @@ export default defineEventHandler(async (event) => {
|
|||||||
const hallenMap = new Map()
|
const hallenMap = new Map()
|
||||||
|
|
||||||
// Debug: Zeige verfügbare Spalten
|
// Debug: Zeige verfügbare Spalten
|
||||||
console.log('Verfügbare Spalten in gefilterten Daten:', Object.keys(filteredData[0] || {}))
|
loggerInfo('Verfügbare Spalten in gefilterten Daten', { columns: Object.keys(filteredData[0] || {}) })
|
||||||
|
|
||||||
filteredData.forEach((row, index) => {
|
filteredData.forEach((row, index) => {
|
||||||
// Suche Halle-Spalten mit verschiedenen möglichen Namen
|
// Suche Halle-Spalten mit verschiedenen möglichen Namen
|
||||||
@@ -150,10 +155,9 @@ export default defineEventHandler(async (event) => {
|
|||||||
const heimMannschaft = row.HeimMannschaft || ''
|
const heimMannschaft = row.HeimMannschaft || ''
|
||||||
|
|
||||||
// Debug: Zeige Halle-Daten für erste paar Zeilen
|
// Debug: Zeige Halle-Daten für erste paar Zeilen
|
||||||
if (index < 3) {
|
if (index < 3) {
|
||||||
// nosemgrep: javascript.lang.security.audit.unsafe-formatstring.unsafe-formatstring
|
loggerInfo('Spielplan-Zeile Halle-Daten', { index, halleName, halleStrasse, hallePLZ, halleOrt, heimMannschaft })
|
||||||
console.log(`Zeile ${index}: HalleName="${halleName}", HalleStrasse="${halleStrasse}", HallePLZ="${hallePLZ}", HalleOrt="${halleOrt}", HeimMannschaft="${heimMannschaft}"`)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (halleName && halleStrasse && hallePLZ && halleOrt) {
|
if (halleName && halleStrasse && hallePLZ && halleOrt) {
|
||||||
const halleKey = `${halleName}|${halleStrasse}|${hallePLZ}|${halleOrt}`
|
const halleKey = `${halleName}|${halleStrasse}|${hallePLZ}|${halleOrt}`
|
||||||
@@ -173,7 +177,7 @@ export default defineEventHandler(async (event) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const hallenListe = Array.from(hallenMap.values())
|
const hallenListe = Array.from(hallenMap.values())
|
||||||
console.log('Gefundene Hallen:', hallenListe.length, hallenListe)
|
loggerInfo('Gefundene Hallen', { count: hallenListe.length, hallen: hallenListe })
|
||||||
|
|
||||||
// Generiere LaTeX-Code für PDF
|
// Generiere LaTeX-Code für PDF
|
||||||
const teamName = team.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase())
|
const teamName = team.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase())
|
||||||
@@ -323,7 +327,7 @@ ${hallenListe.map(halle => {
|
|||||||
await fs.unlink(tempTexFile.replace('.tex', '.log'))
|
await fs.unlink(tempTexFile.replace('.tex', '.log'))
|
||||||
await fs.unlink(tempTexFile.replace('.tex', '.aux'))
|
await fs.unlink(tempTexFile.replace('.tex', '.aux'))
|
||||||
} catch (_error) {
|
} catch (_error) {
|
||||||
console.error('Fehler beim Löschen temporärer Dateien:', error)
|
loggerError('Fehler beim Löschen temporärer Dateien:', { error: _error })
|
||||||
}
|
}
|
||||||
}, 5000)
|
}, 5000)
|
||||||
|
|
||||||
@@ -343,7 +347,7 @@ ${hallenListe.map(halle => {
|
|||||||
return pdfBuffer
|
return pdfBuffer
|
||||||
|
|
||||||
} catch (compileError) {
|
} catch (compileError) {
|
||||||
console.error('LaTeX-Kompilierung fehlgeschlagen:', compileError)
|
loggerError('LaTeX-Kompilierung fehlgeschlagen:', { error: compileError })
|
||||||
|
|
||||||
// Fallback: HTML-Response mit Fehlermeldung
|
// Fallback: HTML-Response mit Fehlermeldung
|
||||||
const errorHtml = `
|
const errorHtml = `
|
||||||
@@ -375,7 +379,7 @@ ${hallenListe.map(halle => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Fehler beim Generieren des Spielplans:', error)
|
loggerError('Fehler beim Generieren des Spielplans:', { error })
|
||||||
|
|
||||||
if (error.statusCode) {
|
if (error.statusCode) {
|
||||||
throw error
|
throw error
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { getCurrentSeasonSlug, listSpielplanSeasons } from '../../utils/spielplan-data.js'
|
import { getCurrentSeasonSlug, listSpielplanSeasons } from '../../utils/spielplan-data.js'
|
||||||
|
import { error as loggerError } from '../../utils/logger.js'
|
||||||
|
|
||||||
export default defineEventHandler(async () => {
|
export default defineEventHandler(async () => {
|
||||||
try {
|
try {
|
||||||
@@ -13,7 +14,7 @@ export default defineEventHandler(async () => {
|
|||||||
defaultSeason
|
defaultSeason
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Fehler beim Laden der Spielplan-Saisons:', error)
|
loggerError('Fehler beim Laden der Spielplan-Saisons:', { error })
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
seasons: [],
|
seasons: [],
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { importSpielplan } from '../utils/spielplan-import.js'
|
import { importSpielplan } from '../utils/spielplan-import.js'
|
||||||
|
import { info as loggerInfo, error as loggerError } from '../utils/logger.js'
|
||||||
|
|
||||||
const TIME_ZONE = 'Europe/Berlin'
|
const TIME_ZONE = 'Europe/Berlin'
|
||||||
const RUN_HOUR = 7
|
const RUN_HOUR = 7
|
||||||
@@ -68,9 +69,9 @@ async function runImport(reason) {
|
|||||||
running = true
|
running = true
|
||||||
try {
|
try {
|
||||||
const result = await importSpielplan()
|
const result = await importSpielplan()
|
||||||
console.log(`[spielplan-import] ${reason}: ${result.matchCount} Spiele importiert (${result.source.season.dateStart} bis ${result.source.season.dateEnd})`)
|
loggerInfo(`[spielplan-import] ${reason}: ${result.matchCount} Spiele importiert`, { range: `${result.source.season.dateStart} - ${result.source.season.dateEnd}` })
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[spielplan-import] Import fehlgeschlagen:', error)
|
loggerError('[spielplan-import] Import fehlgeschlagen:', { error })
|
||||||
} finally {
|
} finally {
|
||||||
running = false
|
running = false
|
||||||
}
|
}
|
||||||
@@ -86,12 +87,12 @@ function scheduleNext() {
|
|||||||
}, delay)
|
}, delay)
|
||||||
|
|
||||||
timer.unref?.()
|
timer.unref?.()
|
||||||
console.log(`[spielplan-import] Naechster Lauf: ${runAt.toISOString()} (${TIME_ZONE} ${String(RUN_HOUR).padStart(2, '0')}:${String(RUN_MINUTE).padStart(2, '0')})`)
|
loggerInfo('[spielplan-import] Naechster Lauf', { runAt: runAt.toISOString(), tz: TIME_ZONE, time: `${String(RUN_HOUR).padStart(2, '0')}:${String(RUN_MINUTE).padStart(2, '0')}` })
|
||||||
}
|
}
|
||||||
|
|
||||||
export default defineNitroPlugin((nitroApp) => {
|
export default defineNitroPlugin((nitroApp) => {
|
||||||
if (process.env.SPIELPLAN_IMPORT_DISABLED === 'true') {
|
if (process.env.SPIELPLAN_IMPORT_DISABLED === 'true') {
|
||||||
console.log('[spielplan-import] Scheduler deaktiviert')
|
loggerInfo('[spielplan-import] Scheduler deaktiviert')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
27
server/utils/logger.js
Normal file
27
server/utils/logger.js
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
export function _formatMessage(level, message, context) {
|
||||||
|
const time = new Date().toISOString()
|
||||||
|
try {
|
||||||
|
const ctx = context ? ` ${JSON.stringify(context)}` : ''
|
||||||
|
return `${time} [${level}] ${message}${ctx}`
|
||||||
|
} catch (_e) {
|
||||||
|
return `${time} [${level}] ${message}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function info(message, context) {
|
||||||
|
console.log(_formatMessage('info', message, context))
|
||||||
|
}
|
||||||
|
|
||||||
|
export function warn(message, context) {
|
||||||
|
console.warn(_formatMessage('warn', message, context))
|
||||||
|
}
|
||||||
|
|
||||||
|
export function error(message, context) {
|
||||||
|
console.error(_formatMessage('error', message, context))
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
info,
|
||||||
|
warn,
|
||||||
|
error
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import { promises as fs } from 'fs'
|
import { promises as fs } from 'fs'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import { getProjectPath, getServerDataPath } from './paths.js'
|
import { getProjectPath, getServerDataPath } from './paths.js'
|
||||||
|
import { error as loggerError, info as loggerInfo } from './logger.js'
|
||||||
|
|
||||||
const SPIELPLAN_HEADERS = [
|
const SPIELPLAN_HEADERS = [
|
||||||
'Termin',
|
'Termin',
|
||||||
@@ -76,10 +77,10 @@ function seasonSlugToLabel(slug) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function logReadError(message, filePath, error) {
|
function logReadError(message, filePath, error) {
|
||||||
console.error(message, { filePath, error })
|
loggerError(message, { filePath, error })
|
||||||
}
|
}
|
||||||
|
|
||||||
function requireSeasonSlug(seasonSlug) {
|
export function requireSeasonSlug(seasonSlug) {
|
||||||
const value = String(seasonSlug || '')
|
const value = String(seasonSlug || '')
|
||||||
if (!SEASON_SLUG_PATTERN.test(value)) {
|
if (!SEASON_SLUG_PATTERN.test(value)) {
|
||||||
throw new Error(`Ungueltiger Spielplan-Saison-Slug: ${value}`)
|
throw new Error(`Ungueltiger Spielplan-Saison-Slug: ${value}`)
|
||||||
@@ -87,6 +88,15 @@ function requireSeasonSlug(seasonSlug) {
|
|||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function validateSeasonSlug(seasonSlug) {
|
||||||
|
try {
|
||||||
|
requireSeasonSlug(seasonSlug)
|
||||||
|
return true
|
||||||
|
} catch (_e) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function normalizeSpielplanCsvPath(csvPath) {
|
function normalizeSpielplanCsvPath(csvPath) {
|
||||||
const value = String(csvPath || '')
|
const value = String(csvPath || '')
|
||||||
if (value.includes('\0') || path.basename(value) !== 'spielplan.csv') {
|
if (value.includes('\0') || path.basename(value) !== 'spielplan.csv') {
|
||||||
|
|||||||
Reference in New Issue
Block a user