import type { NextRequest } from "next/server"; import { z } from "zod"; import { getActor, requireSignedIn } from "@/lib/auth/actor"; import { errorResponse, firstZodMessage, jsonError } from "@/lib/http"; import { ladeLogo } from "@/lib/qr/logo"; import { CONTENT_TYPES, renderPng, renderSvg } from "@/lib/qr/render"; import { downloadFilename, qrRequestSchema, QrRequestError, resolveQrRequest, } from "@/lib/qr/request"; const downloadSchema = qrRequestSchema.extend({ format: z.enum(["png", "svg"], { error: "Ungültiges Dateiformat." }), }); /** Liefert den fertigen QR-Code als PNG- oder SVG-Datei zum Herunterladen. */ export async function POST(request: NextRequest): Promise { try { // Der Generator steht nur Angemeldeten offen; die Weiterleitung // unter /r/{slug} bleibt davon unberührt und öffentlich. requireSignedIn(await getActor()); const parsed = downloadSchema.safeParse(await request.json()); if (!parsed.success) { return jsonError(firstZodMessage(parsed.error)); } const { format, design } = parsed.data; // Ein dynamischer Code ohne Slug wäre eine Weiterleitung ins Leere. if (parsed.data.mode === "dynamic" && !parsed.data.slug) { throw new QrRequestError( "Bitte speichere den dynamischen Code zuerst — erst dann existiert die Kurz-URL.", ); } const { qrType, data, content } = await resolveQrRequest(parsed.data); const logo = await ladeLogo(design); const body = format === "png" ? new Uint8Array(await renderPng(content, design, logo)) : await renderSvg(content, design, logo); return new Response(body, { headers: { "Content-Type": CONTENT_TYPES[format], "Content-Disposition": `attachment; filename="${downloadFilename(qrType, data, format, parsed.data.label)}"`, "Cache-Control": "no-store", }, }); } catch (error) { return errorResponse(error); } }