Files
harheimertc/server/plugins/spielplan-import-scheduler.js
Torsten Schulz (local) e5c247f703
Some checks failed
Code Analysis and Production Deploy / analyze (push) Has been skipped
Code Analysis and Production Deploy / deploy-production (push) Has been skipped
Code Analysis and Production Deploy / deploy-test (push) Successful in 1m49s
Code Analysis and Production Deploy / analyze (pull_request) Failing after 2m37s
Code Analysis and Production Deploy / deploy-production (pull_request) Has been skipped
Code Analysis and Production Deploy / deploy-test (pull_request) Has been skipped
Require Package Version Change / check (pull_request) Failing after 8s
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
2026-05-20 11:20:54 +02:00

109 lines
2.9 KiB
JavaScript

import { importSpielplan } from '../utils/spielplan-import.js'
import { info as loggerInfo, error as loggerError } from '../utils/logger.js'
const TIME_ZONE = 'Europe/Berlin'
const RUN_HOUR = 7
const RUN_MINUTE = 0
const MAX_TIMEOUT = 2_147_483_647
let timer = null
let running = false
function getTimeParts(date) {
const parts = new Intl.DateTimeFormat('en-CA', {
timeZone: TIME_ZONE,
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false
}).formatToParts(date)
return Object.fromEntries(parts.map((part) => [part.type, part.value]))
}
function getTimeZoneOffset(date) {
const parts = getTimeParts(date)
const zonedAsUtc = Date.UTC(
Number(parts.year),
Number(parts.month) - 1,
Number(parts.day),
Number(parts.hour),
Number(parts.minute),
Number(parts.second)
)
return zonedAsUtc - date.getTime()
}
function zonedDateToUtc(year, month, day, hour, minute) {
const utcGuess = new Date(Date.UTC(year, month - 1, day, hour, minute, 0))
const offset = getTimeZoneOffset(utcGuess)
return new Date(utcGuess.getTime() - offset)
}
function nextRunAt(now = new Date()) {
const parts = getTimeParts(now)
let year = Number(parts.year)
let month = Number(parts.month)
let day = Number(parts.day)
let candidate = zonedDateToUtc(year, month, day, RUN_HOUR, RUN_MINUTE)
if (candidate <= now) {
const nextDay = zonedDateToUtc(year, month, day + 1, 12, 0)
const nextParts = getTimeParts(nextDay)
year = Number(nextParts.year)
month = Number(nextParts.month)
day = Number(nextParts.day)
candidate = zonedDateToUtc(year, month, day, RUN_HOUR, RUN_MINUTE)
}
return candidate
}
async function runImport(reason) {
if (running) return
running = true
try {
const result = await importSpielplan()
loggerInfo(`[spielplan-import] ${reason}: ${result.matchCount} Spiele importiert`, { range: `${result.source.season.dateStart} - ${result.source.season.dateEnd}` })
} catch (error) {
loggerError('[spielplan-import] Import fehlgeschlagen:', { error })
} finally {
running = false
}
}
function scheduleNext() {
const runAt = nextRunAt()
const delay = Math.min(Math.max(runAt.getTime() - Date.now(), 1_000), MAX_TIMEOUT)
timer = setTimeout(async () => {
await runImport('taeglicher Lauf')
scheduleNext()
}, delay)
timer.unref?.()
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) => {
if (process.env.SPIELPLAN_IMPORT_DISABLED === 'true') {
loggerInfo('[spielplan-import] Scheduler deaktiviert')
return
}
scheduleNext()
if (process.env.SPIELPLAN_IMPORT_RUN_ON_START === 'true') {
runImport('Startlauf')
}
nitroApp.hooks.hookOnce('close', () => {
if (timer) clearTimeout(timer)
})
})