Worker- und PDF-Dienst

Der Worker hängt am Discord-Gateway und schreibt alle 30 Sekunden sein
Lebenszeichen in die Datenbank, damit /api/health im Web-Container den
Gateway-Zustand kennt, ohne selbst mit Discord zu sprechen. healthcheck.js
prüft genau dieses Lebenszeichen. Der eigentliche Rollen-Abgleich kommt in
Phase 1.

Der PDF-Dienst rendert die Druckansicht über Playwright. Kein PDF-Layout von
Hand, die Gestaltung kommt aus @media print der Seite selbst. Er kennt keine
Rechte: wer darf, entscheidet die Web-App, bevor sie hier anfragt, und schickt
das Sitzungs-Cookie des Abrufers mit, damit die gerenderte Seite dessen
Freigabestufe sieht.
This commit is contained in:
Harlekin
2026-09-01 00:01:49 +02:00
parent ade977538d
commit 8375d1cb03
13 changed files with 374 additions and 0 deletions
+39
View File
@@ -0,0 +1,39 @@
// Healthcheck des Dienstes `worker`, wird vom Compose aufgerufen.
// Prueft das eigene Lebenszeichen in der Datenbank: ist es frisch und meldet
// `up`, gilt die Gateway-Verbindung als in Ordnung. Siehe docs/11-betrieb.md.
// `pg` wird als CommonJS ausgeliefert, deshalb der Default-Import.
import pg from 'pg'
const { Client } = pg
const MAX_AGE_MS = 120_000
const client = new Client({ connectionString: process.env.DIRECT_DATABASE_URL ?? process.env.DATABASE_URL })
try {
await client.connect()
const { rows } = await client.query(
'SELECT status, at FROM "ServiceHeartbeat" WHERE service = $1',
['worker'],
)
const beat = rows[0]
if (!beat) {
console.error('kein Lebenszeichen')
process.exit(1)
}
const ageMs = Date.now() - new Date(beat.at).getTime()
if (ageMs > MAX_AGE_MS) {
console.error(`Lebenszeichen ${Math.round(ageMs / 1000)} s alt`)
process.exit(1)
}
if (beat.status !== 'up') {
console.error(`Zustand ${beat.status}`)
process.exit(1)
}
process.exit(0)
} catch (error) {
console.error(error instanceof Error ? error.message : error)
process.exit(1)
} finally {
await client.end().catch(() => undefined)
}