Files
Harlekin e0b03ea336
Prüfung / Lint, Typen, Tests (push) Canceled after 0s
Prüfung / Images bauen (pdf) (push) Canceled after 0s
Prüfung / Images bauen (web) (push) Canceled after 0s
Prüfung / Images bauen (worker) (push) Canceled after 0s
Images / bauen (pdf, docker/pdf.Dockerfile) (push) Canceled after 0s
Images / bauen (web, docker/web.Dockerfile) (push) Canceled after 0s
Images / bauen (worker, docker/worker.Dockerfile) (push) Canceled after 0s
Gedankenstriche durch Bindestriche ersetzt
375 Vorkommen in 210 Dateien. Betrifft Kommentare, Oberflaechentexte und die
README; an keiner Stelle Bedeutung, nur Zeichensetzung.
2026-09-04 08:54:52 +02:00

64 lines
2.2 KiB
TypeScript

import { createReadStream } from 'node:fs'
import { stat } from 'node:fs/promises'
import { Readable } from 'node:stream'
import { NextResponse } from 'next/server'
import { db } from '@/lib/db'
import { getCurrentUser } from '@/lib/auth/current'
import { recordRead } from '@/lib/audit/write'
import { absolutePath } from '@/lib/files/paths'
export const runtime = 'nodejs'
export const dynamic = 'force-dynamic'
/**
* Herunterladen eines fertigen Exports.
*
* Nur der Auftraggeber selbst. Ein Export ist auf **seine** Stufe gekürzt und
* trägt **seinen** Namen im Wasserzeichen - ihn an jemand anderen auszuliefern
* hiesse, ein Dokument mit fremdem Namen zu verteilen.
*/
export async function GET(
_request: Request,
context: { params: Promise<{ id: string }> },
): Promise<Response> {
const user = await getCurrentUser()
if (!user) return new NextResponse(null, { status: 404 })
const { id } = await context.params
const auftrag = await db.exportJob.findFirst({
where: { id, requestedById: user.id, status: 'READY' },
select: { id: true, kind: true, targetId: true, storageKey: true },
})
if (!auftrag?.storageKey) return new NextResponse(null, { status: 404 })
let groesse: number
try {
groesse = (await stat(absolutePath(auftrag.storageKey))).size
} catch {
return new NextResponse(null, { status: 404 })
}
// Jeder Abruf wird protokolliert, nicht nur die Erzeugung: ein PDF kann
// mehrfach heruntergeladen und weitergegeben werden.
recordRead({
actorId: user.id,
action: 'export.download',
targetType: 'ExportJob',
targetId: auftrag.id,
clearanceAtTime: user.permissions.clearanceLevel,
meta: { kind: auftrag.kind, targetId: auftrag.targetId ?? undefined },
})
const stream = Readable.toWeb(createReadStream(absolutePath(auftrag.storageKey))) as ReadableStream
return new NextResponse(stream, {
headers: {
'Content-Type': 'application/pdf',
'Content-Length': String(groesse),
'X-Content-Type-Options': 'nosniff',
'Cache-Control': 'private, no-store',
'Content-Security-Policy': "default-src 'none'; sandbox",
'Content-Disposition': `attachment; filename="afis-export-${auftrag.id}.pdf"`,
},
})
}