Reservierung aufheben und offene Galerie entschieden
- Aufheben darf der Reservierende selbst (per Token-Link) und der Betreiber. Ein offener release-Endpunkt wäre die naheliegende, aber falsche Variante: dann löscht jeder Besucher fremde Reservierungen, und weil reserved_by mitgeht, bleibt nicht mal nachvollziehbar, dass jemand reserviert hatte. Token wird beim Aufheben und beim Erledigen gelöscht, Vergleich mit compare_digest. - Die Galerie bleibt frei zugänglich, ohne Zugangscode. Damit wird das Rate-Limit zur einzigen Bremse vor dem Reservieren-Endpunkt, und zwei Dinge gehören zwingend dazu: reserved_by erscheint öffentlich nur als "reserviert", und noindex/robots.txt verhindern, dass Fotos und Texte dauerhaft im Suchindex landen. projekt.md bei /release entsprechend präzisiert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GR4bNaj9GtRu57J4Niii8o
This commit is contained in:
@@ -10,11 +10,15 @@ offen waren, und um die Punkte, die durch den Internet-Zugang dazukommen.
|
||||
| Frontend | **Variante A** – Jinja2 + HTMX + Tailwind, ein einziger Dienst, kein Node.js |
|
||||
| Erfassen/Bearbeiten/Löschen | Hinter **einem Passwort** |
|
||||
| Reservieren | **Ohne Anmeldung** |
|
||||
| Reservierung aufheben | **Der Reservierende selbst** (per Token-Link) **und der Betreiber** |
|
||||
| Galerie | **Frei zugänglich**, ohne Zugangscode |
|
||||
| Erreichbarkeit | **Aus dem Internet**, per Reverse-Proxy mit HTTPS |
|
||||
|
||||
Die letzten beiden zusammen sind der eigentliche Knackpunkt dieses Projekts:
|
||||
ein Endpunkt, den jeder im Internet ohne Anmeldung auslösen kann, der den
|
||||
Zustand ändert. Alles unter „Absicherung" ergibt sich daraus.
|
||||
Die Kombination aus „frei zugänglich" und „Reservieren ohne Anmeldung" ist der
|
||||
eigentliche Knackpunkt: ein Endpunkt, den jeder im Internet ohne jede Hürde
|
||||
auslösen kann und der den Zustand ändert. Alles unter „Absicherung" ergibt
|
||||
sich daraus – bei einer offenen Galerie ist das Rate-Limit keine Kür, sondern
|
||||
das Einzige, was zwischen einem Skript und dem kompletten Bestand steht.
|
||||
|
||||
---
|
||||
|
||||
@@ -86,7 +90,8 @@ Das Schema wird grundsätzlich übernommen. Fünf Ergänzungen:
|
||||
|
||||
2. **`reservation_token` (neu) in `items`.** Zufälliger Wert, der beim
|
||||
Reservieren erzeugt wird. Nur wer ihn hat, kann die eigene Reservierung
|
||||
wieder aufheben. Begründung unten unter „Offene Punkte".
|
||||
wieder aufheben; der Betreiber kann es ohnehin. Wird beim Aufheben und
|
||||
beim Erledigen wieder gelöscht. Einzelheiten unter „Absicherung", Punkt 2.
|
||||
|
||||
3. **`sizes` als eigene Tabelle.** `projekt.md` sieht `GET /api/v1/sizes` vor,
|
||||
aber keine Tabelle dazu. Grössen aus den vorhandenen Einträgen zu
|
||||
@@ -176,16 +181,48 @@ bei einem Heimnetz-Dienst. Punkte 1 und 2 sind die wichtigsten.
|
||||
|
||||
### 2. Reservieren ohne Anmeldung
|
||||
|
||||
Ein offener, zustandsändernder Endpunkt im Internet. Ohne Bremse kann ein
|
||||
Skript in Sekunden alles reservieren oder `reserved_by` als Werbefläche
|
||||
missbrauchen.
|
||||
Ein offener, zustandsändernder Endpunkt im Internet – bei frei zugänglicher
|
||||
Galerie ohne jede vorgelagerte Hürde. Ohne Bremse kann ein Skript in Sekunden
|
||||
alles reservieren oder `reserved_by` als Werbefläche missbrauchen.
|
||||
|
||||
- **Rate-Limit pro IP** (`slowapi`), z.B. 5 Reservierungen pro Stunde.
|
||||
- `reserved_by` begrenzen (Länge, keine URLs) und beim Anzeigen maskieren.
|
||||
Jinja2 maskiert von sich aus – entscheidend ist, `|safe` dort nirgends zu
|
||||
verwenden.
|
||||
- Reservierungen sind **jederzeit vom Betreiber aufhebbar** (Admin-Ansicht),
|
||||
damit eine Missbrauchswelle in einem Rutsch aufgeräumt werden kann.
|
||||
- Reservierungen sind **jederzeit vom Betreiber aufhebbar**, auch mehrere auf
|
||||
einmal, damit eine Missbrauchswelle in einem Rutsch aufgeräumt werden kann.
|
||||
|
||||
**Wer darf aufheben – und wie das ohne Konten funktioniert**
|
||||
|
||||
Zwei Wege, entsprechend der Entscheidung oben:
|
||||
|
||||
1. **Der Reservierende selbst.** Beim Reservieren erzeugt die Anwendung ein
|
||||
`reservation_token` (Zufallswert) und zeigt danach einen Link
|
||||
`/items/{id}/release?token=…` – zum Merken oder Weiterschicken. Nur wer
|
||||
ihn hat, kann *diese* Reservierung zurücknehmen.
|
||||
2. **Der Betreiber**, aus der Admin-Ansicht heraus, ohne Token.
|
||||
|
||||
Ein offener `release`-Endpunkt ohne Token wäre die naheliegende, aber falsche
|
||||
Variante: dann könnte jeder Besucher die Reservierung eines anderen löschen –
|
||||
und weil `reserved_by` mitgelöscht wird, bliebe nicht einmal nachvollziehbar,
|
||||
dass überhaupt jemand reserviert hatte.
|
||||
|
||||
Das Token wird beim Aufheben und beim Erledigen (`mark-given`) gelöscht, damit
|
||||
ein alter Link nicht später eine neue Reservierung eines anderen aufhebt.
|
||||
Verglichen wird es mit `secrets.compare_digest`.
|
||||
|
||||
### 2b. Folgen der offenen Galerie
|
||||
|
||||
Die Galerie ist ohne Zugangscode erreichbar. Das ist eine bewusste
|
||||
Entscheidung; zwei Dinge gehören dann aber dazu:
|
||||
|
||||
- **`reserved_by` nicht öffentlich anzeigen** (siehe Abschnitt 5). In der
|
||||
Galerie steht nur „reserviert", der Name erscheint ausschliesslich in der
|
||||
Admin-Ansicht. Ohne Zugangscode wäre der Name sonst für jeden lesbar.
|
||||
- **`noindex` und eine `robots.txt`.** Frei erreichbar heisst nicht, dass die
|
||||
Seite in Suchergebnissen auftauchen muss. Ohne das landen Fotos und Texte
|
||||
im Index von Google und sind auch dann noch auffindbar, wenn die Börse
|
||||
längst abgeräumt ist.
|
||||
|
||||
### 3. Anmeldung fürs Erfassen
|
||||
|
||||
@@ -302,24 +339,11 @@ Gleiches Vorgehen wie bei der Kantone-App
|
||||
|
||||
## Offene Punkte
|
||||
|
||||
1. **Wer darf eine Reservierung wieder aufheben?** `projekt.md` sieht
|
||||
`POST /items/{id}/release` vor, sagt aber nicht, wer ihn aufrufen darf. Ist
|
||||
er offen, kann jeder die Reservierung eines anderen löschen. Vorschlag: beim
|
||||
Reservieren wird ein Token vergeben und als Link angezeigt („Reservierung
|
||||
aufheben") – wer ihn hat, kann die eigene zurücknehmen; der Betreiber kann
|
||||
es ohnehin. Bitte bestätigen, dann kommt `reservation_token` ins Modell.
|
||||
|
||||
2. **Soll die Galerie überhaupt öffentlich sein?** Auch ohne Schreibzugriff
|
||||
zeigt sie Kinderkleidung, Namen und indirekt den Wohnort. Ein einfacher
|
||||
Zugangscode für die ganze Seite (einmal eingeben, bleibt im Cookie) würde
|
||||
Suchmaschinen und Zufallsbesucher aussperren, ohne dass jemand ein Konto
|
||||
braucht. Empfehlung: ja, zusätzlich zu `noindex`. Deine Entscheidung.
|
||||
|
||||
3. **Benachrichtigung bei Reservierung?** Nicht in `projekt.md`. Ohne sie musst
|
||||
1. **Benachrichtigung bei Reservierung?** Nicht in `projekt.md`. Ohne sie musst
|
||||
du selbst nachschauen. Ein einfacher Weg wäre eine Nachricht per E-Mail oder
|
||||
Telegram. Kann auch später kommen – dann aber besser gleich als eigener
|
||||
Meilenstein statt nachträglich eingeschoben.
|
||||
|
||||
4. **Mehrere Kinder / Grössenverläufe?** Aktuell ist alles ein flacher Bestand.
|
||||
2. **Mehrere Kinder / Grössenverläufe?** Aktuell ist alles ein flacher Bestand.
|
||||
Falls du später nach „von wem" oder „Jahrgang" filtern willst, wäre jetzt
|
||||
der günstige Zeitpunkt für ein zusätzliches Feld.
|
||||
|
||||
@@ -79,8 +79,13 @@ Eine schlanke, mobile-optimierte Web-App zum Katalogisieren, Präsentieren und R
|
||||
### Reservierung & Status
|
||||
- `POST /api/v1/items/{id}/reserve`
|
||||
- **Body:** `{"reserved_by": "Familie Meier"}`
|
||||
- **Response:** enthält das `reservation_token` – daraus wird der Link zum
|
||||
Aufheben gebildet.
|
||||
- `POST /api/v1/items/{id}/release`
|
||||
- Setzt Status zurück auf `available` und löscht `reserved_by`.
|
||||
- **Erlaubt für:** den Reservierenden (mit gültigem `token`) oder den
|
||||
angemeldeten Betreiber. Ohne beides: `403`. Sonst könnte jeder Besucher
|
||||
fremde Reservierungen löschen.
|
||||
- `POST /api/v1/items/{id}/mark-given`
|
||||
- Setzt Status auf `given_away`.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user