Ein statischer Code traegt seinen Inhalt selbst — zweimal derselbe Inhalt ergibt zweimal exakt dasselbe Bild. Bisher entstand der zweite Eintrag stillschweigend. Das Anlegen antwortet nun mit 409 und nennt den vorhandenen Eintrag samt Namen und Datum; die Oberflaeche zeigt das als Warnung mit dem Knopf "Trotzdem ablegen". Eine Rueckfrage, keine Sperre — zwei Fassungen desselben Codes koennen gewollt sein. Verglichen wird Typ und Inhalt innerhalb der Gruppe, nicht die Gestaltung. Dynamische Codes bleiben aussen vor: zwei Kurz-Links auf dasselbe Ziel sind der Normalfall, sobald getrennt gezaehlt werden soll. ApiError traegt dafuer jetzt Status und Antwortkoerper — eine gemeldete Dublette ist keine Sackgasse, sondern eine Rueckfrage. Refs #6
62 lines
1.9 KiB
TypeScript
62 lines
1.9 KiB
TypeScript
import "server-only";
|
|
|
|
import { toJsonInput } from "@/lib/codes";
|
|
import { prisma } from "@/lib/db";
|
|
|
|
/**
|
|
* Dublettenprüfung für statische Codes.
|
|
*
|
|
* Ein statischer Code trägt seinen Inhalt selbst — zweimal derselbe Inhalt
|
|
* ergibt zweimal exakt dasselbe Bild. Solche Einträge sind nicht falsch, aber
|
|
* sie fluten die Liste, und beim Wiederfinden weiß niemand mehr, welcher der
|
|
* beiden gemeint war.
|
|
*
|
|
* Bewusst **nur** für statische Codes: Zwei dynamische Codes mit demselben
|
|
* Ziel sind ein üblicher Fall — zwei Kampagnen, zwei Kurz-Links, getrennte
|
|
* Zähler. Da wäre eine Warnung nur lästig.
|
|
*
|
|
* Bewusst **ohne** die Gestaltung: Verglichen wird der Inhalt. Derselbe Inhalt
|
|
* einmal in Schwarz-Weiß für den Druck und einmal in Hausfarben ist zwar
|
|
* bewusst zweimal angelegt — aber eben auch zweimal derselbe Code. Die
|
|
* Warnung sagt das und lässt sich übergehen.
|
|
*/
|
|
export type StaticDuplicate = {
|
|
id: string;
|
|
label: string | null;
|
|
createdAt: string;
|
|
};
|
|
|
|
/**
|
|
* Sucht einen bereits abgelegten statischen Code mit gleichem Typ und
|
|
* gleichem Inhalt in derselben Gruppe — den zuletzt angelegten.
|
|
*
|
|
* Die Gruppe grenzt bewusst ein: Ein Treffer aus einer fremden Gruppe wäre
|
|
* nicht bloß nutzlos, er verriete auch deren Bestand.
|
|
*/
|
|
export async function findStaticDuplicate(
|
|
groupId: string,
|
|
type: string,
|
|
data: Record<string, unknown>,
|
|
): Promise<StaticDuplicate | null> {
|
|
const treffer = await prisma.qrCode.findFirst({
|
|
where: {
|
|
mode: "static",
|
|
type,
|
|
groupId,
|
|
// Postgres vergleicht jsonb nach Struktur, nicht nach Schreibweise —
|
|
// Schlüsselreihenfolge und Leerzeichen spielen keine Rolle.
|
|
data: { equals: toJsonInput(data) },
|
|
},
|
|
orderBy: { createdAt: "desc" },
|
|
select: { id: true, label: true, createdAt: true },
|
|
});
|
|
|
|
if (!treffer) return null;
|
|
|
|
return {
|
|
id: treffer.id,
|
|
label: treffer.label,
|
|
createdAt: treffer.createdAt.toISOString(),
|
|
};
|
|
}
|