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,59 @@
|
||||
import "server-only";
|
||||
|
||||
import QRCode, { type QRCodeRenderersOptions } from "qrcode";
|
||||
|
||||
import type { QrDesign } from "@/lib/qr/design";
|
||||
|
||||
/** Fehler beim Erzeugen des Codes, mit Meldung für die Oberfläche. */
|
||||
export class QrRenderError extends Error {}
|
||||
|
||||
function toOptions(design: QrDesign): QRCodeRenderersOptions {
|
||||
return {
|
||||
errorCorrectionLevel: design.ecLevel,
|
||||
margin: design.margin,
|
||||
width: design.size,
|
||||
color: {
|
||||
dark: design.fgColor,
|
||||
light: design.bgColor,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function wrap(error: unknown): never {
|
||||
const detail = error instanceof Error ? error.message : String(error);
|
||||
if (/too (long|big)|code length overflow|data too long/i.test(detail)) {
|
||||
throw new QrRenderError(
|
||||
"Der Inhalt ist zu lang für einen QR-Code. Kürze die Adresse oder wähle ein niedrigeres Fehlerkorrektur-Level.",
|
||||
);
|
||||
}
|
||||
throw new QrRenderError(`Der QR-Code konnte nicht erzeugt werden: ${detail}`);
|
||||
}
|
||||
|
||||
export async function renderSvg(
|
||||
content: string,
|
||||
design: QrDesign,
|
||||
): Promise<string> {
|
||||
try {
|
||||
return await QRCode.toString(content, { ...toOptions(design), type: "svg" });
|
||||
} catch (error) {
|
||||
wrap(error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function renderPng(
|
||||
content: string,
|
||||
design: QrDesign,
|
||||
): Promise<Buffer> {
|
||||
try {
|
||||
return await QRCode.toBuffer(content, { ...toOptions(design), type: "png" });
|
||||
} catch (error) {
|
||||
wrap(error);
|
||||
}
|
||||
}
|
||||
|
||||
export type QrFormat = "png" | "svg";
|
||||
|
||||
export const CONTENT_TYPES: Record<QrFormat, string> = {
|
||||
png: "image/png",
|
||||
svg: "image/svg+xml",
|
||||
};
|
||||
Reference in New Issue
Block a user