fix(admin): Kategorie und Slug beim Produkt-Anlegen serverseitig pruefen

Die Kategorie kam bisher nur per Typ-Cast aus dem Formular; ein
manipulierter Wert endete als unbehandelte Prisma-Ausnahme. Ein eigener
Slug wurde ungeprueft zur oeffentlichen Adresse, inklusive Schraegstrichen
und Sonderzeichen. Neues adminProductIdentitySchema: Kategorie per Enum,
Slug auf a-z, 0-9 und Bindestrich (Umlaute umschrieben, max. 80 Zeichen),
leere Adresse wird mit Meldung abgelehnt.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
Nick SzaunigandClaude Opus 5.5 committed 2026-09-25 20:43:54 +02:00
1 parent c125e91941
commit 95b4b5953d
3 files changed
+68 -4

No files matched your search

+33
View File
@@ -129,6 +129,39 @@ export const adminProductNumbersSchema = z.object({
});
export type AdminProductNumbersInput = z.infer<typeof adminProductNumbersSchema>;
const PRODUCT_SLUG_MAX_LENGTH = 80;
/** Slug als oeffentliche Adresse (/honig/<slug>): nur a-z, 0-9 und Bindestrich. */
function toProductSlug(value: string): string {
return value
.toLowerCase()
.replace(/ä/g, "ae")
.replace(/ö/g, "oe")
.replace(/ü/g, "ue")
.replace(/ß/g, "ss")
.replace(/[^a-z0-9]+/g, "-")
.replace(/^-+|-+$/g, "")
.slice(0, PRODUCT_SLUG_MAX_LENGTH)
.replace(/-+$/, "");
}
// Kategorie und Slug beim Anlegen eines Produkts. Frueher kam die Kategorie
// nur per Typ-Cast aus dem Formular (ein manipulierter Wert endete als
// unbehandelte Prisma-Ausnahme), und ein eigener Slug wurde ungeprueft zur
// oeffentlichen Adresse — samt Schraegstrichen und Sonderzeichen.
export const adminProductIdentitySchema = z
.object({
name: z.string().trim().min(1, "Bitte einen Namen angeben."),
slug: z.string().trim().default(""),
category: z.enum(["HONIG", "KERZEN", "PROPOLIS"], { message: "Unbekannte Kategorie." }),
})
.transform((input) => ({ ...input, slug: toProductSlug(input.slug || input.name) }))
.refine((input) => input.slug.length > 0, {
message: "Aus Name bzw. Slug laesst sich keine Adresse bilden. Bitte einen Slug mit Buchstaben oder Ziffern eintragen.",
path: ["slug"],
});
export type AdminProductIdentityInput = z.infer<typeof adminProductIdentitySchema>;
export const shippingTierSchema = z
.object({
minQuantity: z.coerce.number().int().min(0, "Die Mindestmenge darf nicht negativ sein."),