V1: Next.js-App mit QR-Typ-Registry, Website-Generator und dynamischen Codes
- Erweiterbare Registry aller 15 QR-Typen; nur "Website" ist aktiv, die
uebrigen erscheinen als Platzhalter mit Badge "Bald verfuegbar".
- defineQrType() kapselt Zod-Schema, Kodierung und Redirect-Ziel je Typ,
sodass ein V2-Typ nur aus einem Registry-Eintrag plus Formular besteht.
- Serverseitige QR-Erzeugung (PNG/SVG) mit Farben, Groesse, Fehlerkorrektur
und Rand; Live-Vorschau ueber /api/qr/preview.
- Dynamische Codes: Kurz-URL {BASE_URL}/r/{slug}, 302-Redirect mit
Scan-Zaehler, CRUD unter /api/codes.
- Prisma 7 mit pg-Driver-Adapter und Initial-Migration fuer PostgreSQL.
- Deutsche Oberflaeche mit Tailwind CSS, helles Theme, responsiv.
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
import type { NextRequest } from "next/server";
|
||||
import { z } from "zod";
|
||||
|
||||
import { errorResponse, firstZodMessage, jsonError } from "@/lib/http";
|
||||
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<Response> {
|
||||
try {
|
||||
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 { module, data, content } = resolveQrRequest(parsed.data);
|
||||
|
||||
const body =
|
||||
format === "png"
|
||||
? new Uint8Array(await renderPng(content, design))
|
||||
: await renderSvg(content, design);
|
||||
|
||||
return new Response(body, {
|
||||
headers: {
|
||||
"Content-Type": CONTENT_TYPES[format],
|
||||
"Content-Disposition": `attachment; filename="${downloadFilename(module, data, format)}"`,
|
||||
"Cache-Control": "no-store",
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
return errorResponse(error);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user