feat(tags): QR-Aufkleber, Geraeteseite, Etikettendruck und Scanner
Prüfung / pruefung (pull_request) Canceled after 0s
Prüfung / pruefung (push) Canceled after 0s
Prüfung / e2e (pull_request) Canceled after 0s
Prüfung / e2e (push) Canceled after 0s

Der Einstieg, fuer den die Anwendung gebaut ist: scannen, sehen was zu tun ist,
loslegen. `/t/<tagCode>` kennt drei Faelle — registriertes Geraet, vorgedruckter
Tag, unbekannter Code —, die Geraeteseite zeigt Zuweisungen, Schnellstart und
Historie, und beides ist am Handy bedienbar.

Im QR steht ausschliesslich `{APP_URL}/t/{tagCode}`: kein Name, keine
Seriennummer, kein Besitzer. Ein Aufkleber haengt jahrelang sichtbar am Geraet
und wird von jedem Telefon gelesen; was darauf steht, ist veroeffentlicht. Die
Masse sind gemessen, nicht behauptet: ein echtes PDF liefert 62 x 29 mm und A4,
und ein bei 300 dpi gerastertes Etikett wird mit demselben Decoder
zurueckgelesen, den auch der Scanner benutzt.

Drei Defekte zeigte erst der gebaute Stand, alle an den Naehten zwischen den
parallel gebauten Teilen: „Starten“ setzte IN_ARBEIT und sprang nicht, „Vorlage
zuweisen“ bot Vorlagen an, die der Dienst danach ablehnte, und die Geraeteliste
verlinkte nichts. Der unabhaengige Review fand drei Blocker — darunter, dass der
Scanner nie die native BarcodeDetector-API benutzte und dass ein Unterpfad in
APP_URL aus dem gedruckten QR herausfiel.

Closes #14

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Florian Wilde
2026-09-25 21:30:12 +02:00
co-authored by Claude Opus 5
parent c78a6ac1af
commit 5f1b0d1435
58 changed files with 4828 additions and 54 deletions
+40
View File
@@ -200,6 +200,46 @@ hat. Bei verstecktem Tab pausiert die Abfrage.
Der Route Handler prüft sein Recht selbst und gibt einen fremden Lauf als „nicht gefunden“ zurück.
Die Hülle unter `src/app/(app)/layout.tsx` deckt `src/app/api/` nicht ab.
## 5e. Der Aufkleber ist die Schnittstelle
Jedes Objekt traegt einen QR-Aufkleber mit einem `tagCode`, der **nie geaendert
und nie wiederverwendet** wird — auch nicht, nachdem das Geraet geloescht wurde
(`TagCodeVergabe`, #006). Der Aufkleber ist damit die einzige Angabe am Geraet,
die Jahre haelt: Hostnamen aendern sich, Besitzer wechseln, Seriennummern werden
abgeschrieben.
**Im QR steht ausschliesslich `{APP_URL}/t/{tagCode}`.** Kein Name, keine
Seriennummer, kein Besitzer. Ein Aufkleber haengt sichtbar am Geraet und wird von
jedem Telefon gelesen, auch von einem fremden; was darauf steht, ist
veroeffentlicht. Die Adresse selbst verraet nichts — dahinter verlangt
`/t/[tagCode]` eine Anmeldung und zeigt einem fremden Kunden dieselbe Antwort wie
bei einem Code, den es nicht gibt.
Drei Dinge daran sind nicht mehr zu korrigieren, sobald gedruckt ist, und stehen
deshalb fest:
1. **Die Basisadresse kommt aus `APP_URL`, mit ihrem Pfad.** Liegt die Anwendung
unter `https://firma.de/laufzettel`, muss genau das im QR stehen;
`basisAdresse()` in `src/lib/adressen.ts` haelt den Pfad fest, `tagAdresse()`
haengt relativ an. Ohne gesetzte `APP_URL` warnt die Druckseite, statt still
`localhost` zu drucken.
2. **Der QR entsteht lokal** (`qrcode`, Fehlerkorrektur M, Ruhezone 4). Eine
externe API bekaeme mit jedem Aufruf den Bestand an Tag-Codes geliefert.
3. **Die Masse sind geprueft, nicht behauptet.** `tests/e2e/etiketten.spec.ts`
misst die Seitengroesse an einem echten PDF (62 × 29 mm bzw. A4) und liest
ein bei 300 dpi gerastertes Etikett mit demselben Decoder zurueck, den auch
der Scanner benutzt.
Der Scanner laedt `barcode-detector/polyfill`: wo der Browser eine
`BarcodeDetector`-API mitbringt, laeuft sie, sonst springt WebAssembly ein. Dessen
`.wasm` kommt aus dem eigenen Haus (`scripts/wasm-bereitstellen.mjs` legt es unter
`public/zxing/`), weil die Content-Security-Policy nur `connect-src 'self'`
erlaubt und die Anwendung ohne Internet laufen soll; dafuer traegt `script-src`
ein `'wasm-unsafe-eval'` — die schmale Erlaubnis, WebAssembly zu uebersetzen, und
**nicht** `unsafe-eval`. Laeuft die Kamera nicht oder faellt der Decoder aus, steht
an derselben Stelle ein Feld fuer den abgetippten Code: der steht lesbar unter
jedem QR, und ein Scanner, der nicht startet, darf keine Sackgasse sein.
## 6. Betrieb: zwei Wege, eine Regel
Die Datenbank ist auf beiden Wegen von außen nicht erreichbar: