/** * Nimmt die Bilder für das Handbuch auf. * * Startet eine Wegwerf-Instanz in einem eigenen Compose-Projekt mit eigener * Datenbank, spielt ausschließlich erfundene Daten ein, fährt die Seiten mit * Playwright ab und räumt am Ende alles wieder weg — auch wenn unterwegs etwas * schiefgeht. * * An eine Instanz mit echten Daten kommt dieser Ablauf nicht heran: die * Datenbank entsteht neu, wird nur über ihren eigenen Port angesprochen und * samt Volume wieder entfernt. * * Aufruf: npm run screenshots */ import { spawnSync } from 'node:child_process' import { fileURLToPath } from 'node:url' import webpush from 'web-push' const WURZEL = fileURLToPath(new URL('..', import.meta.url)) const PROJEKT = 'planer-screenshots' const COMPOSE_DATEI = 'docker-compose.screenshots.yml' const APP_PORT = process.env.SCREENSHOT_APP_PORT ?? '3200' const DB_PORT = process.env.SCREENSHOT_DB_PORT ?? '5455' // Frisches Paar je Lauf: Die Bilder sollen die eingerichtete Anwendung // zeigen, ohne dass irgendwo ein privater Schluessel liegen bleibt. const VAPID = webpush.generateVAPIDKeys() const UMGEBUNG = { ...process.env, SCREENSHOT_APP_PORT: APP_PORT, SCREENSHOT_DB_PORT: DB_PORT, SCREENSHOT_VAPID_OEFFENTLICH: VAPID.publicKey, SCREENSHOT_VAPID_PRIVAT: VAPID.privateKey, } function meldung(text: string): void { console.log(`▸ ${text}`) } function laufen( befehl: string, argumente: string[], zusatz: Record = {}, ): number { const ergebnis = spawnSync(befehl, argumente, { cwd: WURZEL, stdio: 'inherit', env: { ...UMGEBUNG, ...zusatz }, }) return ergebnis.status ?? 1 } function compose(argumente: string[]): number { return laufen('docker', ['compose', '-p', PROJEKT, '-f', COMPOSE_DATEI, ...argumente]) } function node(skript: string, zusatz: Record): number { return laufen( process.execPath, [`${WURZEL}node_modules/tsx/dist/cli.mjs`, skript], zusatz, ) } async function wartenAufGesundheit(sekunden: number): Promise { const ziel = `http://127.0.0.1:${APP_PORT}/api/health` const frist = Date.now() + sekunden * 1000 while (Date.now() < frist) { try { const antwort = await fetch(ziel) if (antwort.ok) { const inhalt = (await antwort.json()) as { status?: string } if (inhalt.status === 'ok') return true } } catch { // Noch nicht da, gleich noch einmal. } await new Promise((weiter) => setTimeout(weiter, 2000)) } return false } function aufraeumen(): void { meldung('Wegwerf-Instanz entfernen.') compose(['down', '-v', '--remove-orphans']) } async function main(): Promise { if (laufen('docker', ['info']) !== 0) { console.error('Der Docker-Daemon läuft nicht.') return 1 } // Reste eines abgebrochenen Laufs zuerst wegräumen. compose(['down', '-v', '--remove-orphans']) meldung(`Wegwerf-Instanz bauen und starten (App auf ${APP_PORT}, DB auf ${DB_PORT}).`) if (compose(['up', '-d', '--build']) !== 0) { console.error('Die Wegwerf-Instanz ließ sich nicht starten.') return 1 } meldung('Auf die Anwendung warten.') if (!(await wartenAufGesundheit(180))) { console.error('Die Anwendung ist nicht gesund geworden.') compose(['logs', '--tail', '50', 'app']) return 1 } meldung('Erfundene Daten einspielen.') const demoUrl = `postgresql://demo:demo@127.0.0.1:${DB_PORT}/demo?schema=public` if (node('prisma/demo-seed.ts', { DATABASE_URL: demoUrl, DEMO_MODUS: 'true' }) !== 0) { console.error('Der Demo-Seed ist fehlgeschlagen.') return 1 } meldung('Bilder aufnehmen.') const playwright = laufen( process.execPath, [ `${WURZEL}node_modules/@playwright/test/cli.js`, 'test', '--config', 'playwright.screenshots.config.ts', ], { SCREENSHOT_APP_PORT: APP_PORT }, ) if (playwright !== 0) { console.error('Die Aufnahme ist fehlgeschlagen.') return 1 } meldung('Fertig. Die Bilder liegen in docs/bilder/.') return 0 } let code = 1 try { code = await main() } catch (fehler) { console.error('Der Screenshot-Lauf ist abgebrochen:', fehler) } finally { aufraeumen() } process.exit(code)