Phase C: die letzten sechs Typen — PDF, Bild, Feedback und Datei-Upload

Damit sind alle 15 QR-Typen gebaut.

PDF und Bild brauchen eine hochgeladene Datei. Die liegt in der Datenbank,
nicht auf der Platte: damit bleibt es bei einem Volumen, das gesichert werden
muss — ein zweites wird beim Sichern zu leicht vergessen. Geprueft wird der
Dateiinhalt, nicht die Endung; eine Textdatei namens harmlos.pdf kommt nicht
durch. SVG ist bewusst nicht erlaubt, weil es Skripte enthalten darf und von
derselben Adresse wie die Anwendung ausgeliefert wuerde. Ausgeliefert wird mit
nosniff und einer CSP-Sandbox, damit auch ein PDF mit Skripten nichts anrichtet.

Dateien haengen am Code: wird er geloescht, geht sie mit. Eine ausgetauschte
oder nie gespeicherte Datei wird nach 24 Stunden weggeraeumt. Eine fremde
Datei laesst sich nicht an den eigenen Code haengen — sonst koennte man sie
einem anderen Code entziehen.

Feedback sammelt Note und Kommentar, ohne Name, IP oder Geraetekennung. Die
Auswertung mit Durchschnitt, Verteilung und Kommentaren ist aus der Codeliste
erreichbar — ein Feedback-Code ohne Auswertung waere nur die halbe Sache.
This commit is contained in:
Florian Wilde
2026-08-14 23:34:28 +02:00
parent fdf6452b35
commit e296b1e6c2
32 changed files with 1819 additions and 37 deletions
+83 -11
View File
@@ -3,9 +3,8 @@
Eine Webanwendung zum Erzeugen von QR-Codes — als Docker-Compose-Stack aus
zwei Containern: der Next.js-Anwendung und einer PostgreSQL-Datenbank.
Umgesetzt sind **9 der 15 QR-Typen**. Die übrigen sechs sind in der Oberfläche
sichtbar, als Platzhalter markiert und lassen sich über die Verwaltung
ausblenden.
Umgesetzt sind **alle 15 QR-Typen**. Welche davon in der Oberfläche erscheinen,
entscheidet die Verwaltung — nicht gebrauchte lassen sich ausblenden.
---
@@ -152,9 +151,10 @@ Für einen Code, dessen Ziel du später ändern kannst:
### Schritt 8 — Aufräumen, was du nicht brauchst
Unter **Verwaltung → QR-Typen** kannst du einzelne Typen ausblenden — etwa
die sechs, die noch nicht umgesetzt sind, damit sie gar nicht erst
auftauchen.
Unter **Verwaltung → QR-Typen** kannst du einzelne Typen ausblenden — 15
Kacheln sind viel, wenn ihr ohnehin nur drei davon braucht. Ausgeblendete
Typen verschwinden aus der Auswahl; bereits erstellte Codes dieses Typs
funktionieren unverändert weiter.
### Die wichtigsten Befehle im Betrieb
@@ -358,8 +358,7 @@ werden — der Hostname darin bleibt `db` (der Servicename aus
### 1. Typ wählen
Die Startseite zeigt unter „Was möchtest du erstellen?“ ein Kachel-Grid der
freigegebenen QR-Typen. Umgesetzte Typen sind anklickbar, die übrigen tragen
das Badge „Bald verfügbar“.
freigegebenen QR-Typen. Alle 15 Typen sind umgesetzt.
| Typ | Kodiert als | Statisch | Dynamisch |
| --- | --- | :-: | :-: |
@@ -368,10 +367,16 @@ das Badge „Bald verfügbar“.
| SMS | `SMSTO:` | ✓ | ✓ |
| WhatsApp | `https://wa.me/…` | ✓ | ✓ |
| Standort | `geo:` — dynamisch über OpenStreetMap | ✓ | ✓ |
| Video | die Videoadresse — dynamisch mit Player-Seite | ✓ | ✓ |
| vCard | vCard 3.0 | ✓ | — |
| WLAN | `WIFI:` | ✓ | — |
| App-Link | Weiterleitung je nach Gerät | — | ✓ |
| Zahlung | GiroCode (EPC069-12) oder Zahlungslink | ✓ | nur Link |
| Mehrere Links | eigene Seite mit Linkliste | — | ✓ |
| Social Media | eigene Seite mit den Profilen | — | ✓ |
| PDF | hinterlegte Datei auf eigener Seite | — | ✓ |
| Bild | hinterlegte Bilder auf eigener Seite | — | ✓ |
| Feedback | Bewertungsformular auf eigener Seite | — | ✓ |
Warum manche Typen nur eine Betriebsart können:
@@ -386,6 +391,12 @@ Warum manche Typen nur eine Betriebsart können:
dynamischer Code funktioniert daher nur die Variante „Zahlungslink“ — die
Anwendung weist das beim Speichern ausdrücklich ab, statt einen Code
entstehen zu lassen, der beim Scannen ins Leere führt.
- **Mehrere Links, Social Media, PDF, Bild und Feedback** brauchen eine Seite,
die die Anwendung selbst ausliefert — eine Linkliste oder ein Formular passt
nicht in einen einzelnen Code. Diese Seiten liegen unter `/s/{slug}`; der
gedruckte Code zeigt weiterhin auf `/r/{slug}` und wird dorthin
weitergeleitet. Der Scan-Zähler zählt dabei den Aufruf des Codes, nicht das
Neuladen der Seite.
> **Zum WLAN-Code:** Das Passwort steht im Klartext im Code — wer ihn scannt
> oder abfotografiert, kennt es. Für Gästenetze ist das gewollt. Besteht ein
@@ -482,9 +493,70 @@ Kartenansicht.
Statische Codes erscheinen hier nicht, weil sie nicht gespeichert werden.
> Version 1 ist für den Einzelplatz-/LAN-Betrieb gedacht und hat **keine
> Benutzerverwaltung**. Wer die Anwendung erreicht, sieht und ändert alle
> Codes. Die App gehört daher nicht ungeschützt ins offene Internet.
Bei einem **Feedback-Code** führt aus der Zeile ein Stern-Symbol zur
Auswertung: Durchschnitt, Verteilung der Noten und die abgegebenen Kommentare.
---
### 7. Eigene Seiten: Links, Profile, Dateien, Feedback
Fünf Typen haben kein fremdes Ziel, auf das sich weiterleiten ließe — sie
brauchen eine Seite. Die liefert die Anwendung selbst aus, unter
`{BASE_URL}/s/{slug}`:
| Typ | Was die Seite zeigt |
| --- | --- |
| Mehrere Links | Überschrift, kurzer Text, bis zu 20 beschriftete Links |
| Social Media | dieselbe Form, aber mit Netzwerk-Auswahl aus einem Katalog |
| PDF | das hinterlegte Dokument mit „Öffnen“ und „Herunterladen“ |
| Bild | ein Bild oder eine Galerie aus bis zu 10 Bildern mit Bildunterschriften |
| Feedback | ein Formular mit Sternen oder Gesichtern und optionalem Kommentar |
Diese Seiten sind **öffentlich** und zeigen bewusst nichts von der Anwendung:
keine Navigation, kein „Verwaltung“, kein Hinweis auf den Generator. Wer einen
Aushang scannt, soll nur den Inhalt sehen.
Der gedruckte Code zeigt weiterhin auf `/r/{slug}`; von dort geht es auf die
Seite. Der Scan-Zähler zählt dabei den Aufruf des Codes — ein Neuladen der
Seite zählt nicht mit.
**Bei Social Media** genügt als Eingabe das Kürzel: aus `@firma` wird beim
Speichern `https://instagram.com/firma`. Eine vollständige Adresse bleibt
unverändert, damit auch abweichende Profil-Adressen möglich sind.
**Beim Video-Typ** wird der Player von YouTube oder Vimeo erst nach einem Klick
geladen, und davor steht, dass dabei Daten an den Anbieter gehen. Ungefragt
einen Drittanbieter einzubinden wäre auf einer Seite, die Fremde nach einem
Scan zu sehen bekommen, nicht in Ordnung.
#### Hochgeladene Dateien
PDFs und Bilder liegen **in der Datenbank**, nicht auf der Platte. Damit gibt
es weiterhin genau ein Volumen zu sichern: Wer die Datenbank sichert, hat
alles.
- Die Grenze steht in der `.env` unter `MAX_UPLOAD_MB` (Voreinstellung 10).
- Erlaubt sind PDF sowie PNG, JPEG, WebP, GIF und AVIF. **SVG bewusst nicht** —
eine SVG-Datei darf Skripte enthalten und würde von derselben Adresse wie die
Anwendung ausgeliefert.
- Geprüft wird der **Inhalt**, nicht die Dateiendung. Eine Textdatei mit dem
Namen `harmlos.pdf` wird abgewiesen.
- Wird ein Code gelöscht, verschwindet seine Datei mit ihm. Eine ausgetauschte
Datei und eine hochgeladene, aber nie gespeicherte werden nach 24 Stunden
aufgeräumt.
#### Rückmeldungen
Ein Feedback-Eintrag besteht aus einer Note von 1 bis 5 und einem optionalen
Text. Gespeichert wird **kein Name, keine IP-Adresse, keine Gerätekennung** —
wer einen Aushang scannt und eine Note vergibt, bleibt anonym. Gegen Fluten
gibt es eine Bremse von 10 Rückmeldungen pro Minute und Absender; die Adresse
dient dabei nur als Schlüssel im Arbeitsspeicher und wird nirgends gespeichert.
> Ohne eingeschaltete Anmeldung sieht und ändert jeder, der die Anwendung
> erreicht, alle Codes. Für den Einzelplatz- oder LAN-Betrieb ist das gewollt;
> ins offene Internet gehört die Anwendung dann nicht. Mit `AUTH_ENABLED=true`
> gibt es Benutzer, Gruppen und Rollen — siehe unten.
---