feat(kalender): Hintergrundjobs für Abgleich und Aufräumen
Alle 15 Minuten abgleichen, nachts alte Termine und abgelaufene WebAuthn-Herausforderungen entfernen. Ohne das Aufräumen bliebe der Terminspiegel für immer stehen. Der Riegel über globalThis ist nötig, weil Next im Entwicklungsbetrieb Module mehrfach lädt — sonst liefen nach ein paar Änderungen ein Dutzend Zeitpläne parallel. Im Demo-Modus starten gar keine Jobs: die Screenshot-Instanz soll sich nicht selbst verändern, während sie abfotografiert wird. Protokolliert werden nur Zahlen, nie Kontonamen oder Termininhalte. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
0c67b73a62
commit
42c98c8b29
@@ -0,0 +1,15 @@
|
|||||||
|
/**
|
||||||
|
* Wird von Next einmal beim Hochfahren des Servers ausgeführt.
|
||||||
|
*
|
||||||
|
* Nur in der Node-Laufzeit: im Edge-Umfeld gibt es weder Prisma noch
|
||||||
|
* node-cron. Der Demo-Modus startet bewusst keine Jobs — die
|
||||||
|
* Screenshot-Instanz soll sich nicht selbst verändern, während sie
|
||||||
|
* abfotografiert wird.
|
||||||
|
*/
|
||||||
|
export async function register(): Promise<void> {
|
||||||
|
if (process.env.NEXT_RUNTIME !== 'nodejs') return
|
||||||
|
if (process.env.DEMO_MODUS === 'true') return
|
||||||
|
|
||||||
|
const { jobsStarten } = await import('@/server/jobs')
|
||||||
|
jobsStarten()
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
import 'server-only'
|
||||||
|
import cron from 'node-cron'
|
||||||
|
import { db } from '@/server/db'
|
||||||
|
import { abgleichFenster, kontoAbgleichen } from '@/server/kalender/abgleich'
|
||||||
|
import { abgelaufeneChallengesEntfernen } from '@/server/auth/passkey'
|
||||||
|
import { jetzt } from '@/server/zeitgeber'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hintergrundjobs.
|
||||||
|
*
|
||||||
|
* Werden einmal je Prozess gestartet. Im Entwicklungsbetrieb lädt Next Module
|
||||||
|
* mehrfach neu — deshalb der Riegel über globalThis, sonst liefen nach ein
|
||||||
|
* paar Änderungen ein Dutzend Zeitpläne parallel.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const globalerBereich = globalThis as unknown as { planerJobs?: boolean }
|
||||||
|
|
||||||
|
/** Alle 15 Minuten abgleichen; öfter bringt nichts und kostet nur Aufrufe. */
|
||||||
|
const ABGLEICH_PLAN = '*/15 * * * *'
|
||||||
|
/** Einmal nachts aufräumen. */
|
||||||
|
const AUFRAEUM_PLAN = '17 3 * * *'
|
||||||
|
|
||||||
|
async function alleKontenAbgleichen(): Promise<void> {
|
||||||
|
const zeitpunkt = jetzt()
|
||||||
|
const konten = await db.kalenderkonto.findMany({
|
||||||
|
where: { aktiv: true },
|
||||||
|
select: { id: true },
|
||||||
|
})
|
||||||
|
|
||||||
|
let ok = 0
|
||||||
|
let gescheitert = 0
|
||||||
|
for (const konto of konten) {
|
||||||
|
const bericht = await kontoAbgleichen(konto.id, zeitpunkt)
|
||||||
|
if (bericht.ok) ok++
|
||||||
|
else gescheitert++
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gescheitert > 0) {
|
||||||
|
// Nur Zahlen, keine Namen und keine Termininhalte.
|
||||||
|
console.warn(
|
||||||
|
`Kalenderabgleich: ${ok} Konto/Konten erfolgreich, ${gescheitert} fehlgeschlagen.`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Entfernt Termine, die aus dem Abgleichfenster herausgewandert sind.
|
||||||
|
* Ohne das bliebe der Spiegel für immer stehen.
|
||||||
|
*/
|
||||||
|
async function alteTermineEntfernen(): Promise<void> {
|
||||||
|
const fenster = abgleichFenster(jetzt())
|
||||||
|
await db.termin.deleteMany({ where: { ende: { lt: fenster.von } } })
|
||||||
|
}
|
||||||
|
|
||||||
|
export function jobsStarten(): void {
|
||||||
|
if (globalerBereich.planerJobs) return
|
||||||
|
globalerBereich.planerJobs = true
|
||||||
|
|
||||||
|
cron.schedule(ABGLEICH_PLAN, () => {
|
||||||
|
void alleKontenAbgleichen().catch((fehler: unknown) => {
|
||||||
|
console.error('Kalenderabgleich abgebrochen:', fehler)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
cron.schedule(AUFRAEUM_PLAN, () => {
|
||||||
|
void Promise.all([alteTermineEntfernen(), abgelaufeneChallengesEntfernen(jetzt())])
|
||||||
|
.then(() => undefined)
|
||||||
|
.catch((fehler: unknown) => {
|
||||||
|
console.error('Aufräumen abgebrochen:', fehler)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user