Sprechende Kurz-Links: selbst vergeben, aendern, frueher gueltig halten

Schliesst Issue #1. Der Wunsch betraf den Kurz-Link, nicht das Ziel: Er soll
sich von Anfang an bestimmen und spaeter aendern lassen.

Beim Anlegen gibt es dafuer ein Feld, im Bearbeiten-Dialog laesst er sich
aendern. Erlaubt sind Kleinbuchstaben, Ziffern, Bindestriche und „/" als
Trenner — damit sind Adressen wie kunde-a/sommer moeglich. Umlaute bleiben
draussen, weil sie in Adressen unleserlich kodiert werden; Gross- und
Kleinschreibung ist beim Aufrufen egal.

Beim Aendern bleibt die vorherige Adresse gueltig. Das ist der Kern: Auf
gedruckten Codes steht die alte, und genau deren Bestand ist der Sinn eines
dynamischen Codes. Frueher vergebene Adressen stehen im Dialog und lassen
sich einzeln freigeben — fuer den Tippfehler, mit dem noch nichts gedruckt
ist. Solange eine Adresse gehalten wird, kann sie kein anderer Code
uebernehmen; sonst liessen sich fremde gedruckte Codes umlenken.

Vergeben und Aendern ist standardmaessig Administratoren vorbehalten. Ein
Kurz-Link ist eine oeffentliche Adresse, und wer ihn aendert, kann Aushaenge
entwerten. Unter Verwaltung -> Einstellungen laesst sich das fuer Bearbeiter
freigeben; ohne das Recht erscheint das Feld gar nicht, Codes anlegen und
deren Ziel aendern geht weiterhin. Eigene Aktion in der Policy statt Teil von
codes:update, weil sie mehr wiegt.

Technisch: /r und /s sind Sammelrouten, weil ein Kurz-Link „/" enthalten
darf. Die Eindeutigkeit gilt zusaetzlich auf der kleingeschriebenen Form; die
Migration bricht ab, falls der Bestand das nicht zulaesst, statt still Daten
zu verlieren. Der bestehende exakte Index bleibt — er traegt den haeufigen
Fall, den gescannten Code.
This commit is contained in:
Florian Wilde
2026-08-17 10:41:57 +02:00
parent c38b27ca85
commit 46e7eb0b59
24 changed files with 917 additions and 49 deletions
+2
View File
@@ -31,6 +31,7 @@ export type QrCodeWithRelations = QrCode & {
group?: { name: string } | null;
domain?: { hostname: string } | null;
createdBy?: { name: string | null; email: string } | null;
aliases?: { slug: string }[];
};
export function toQrCodeDto(code: QrCodeWithRelations): QrCodeDto {
@@ -60,6 +61,7 @@ export function toQrCodeDto(code: QrCodeWithRelations): QrCodeDto {
return {
id: code.id,
slug: code.slug,
aliases: (code.aliases ?? []).map((alias) => alias.slug),
type: code.type as QrTypeId,
label: code.label,
data,