Files
kleiderboerse/projekt.md
T
StefanandClaude Opus 5 3cf6903df5 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
2026-08-29 21:50:34 +02:00

139 lines
5.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 📦 Kinderkleider-Börse (Web-App)
Eine schlanke, mobile-optimierte Web-App zum Katalogisieren, Präsentieren und Reservieren von zu klein gewordener Kinderkleidung für Familie, Freunde und Nachbarn.
---
## 🚀 Zielsetzungs & Kern-Features
- **Schnelles Erfassen (Mobile First):** Foto direkt mit dem Smartphone aufnehmen, Grösse & Kategorie wählen, fertig.
- **Übersichtliche Galerie & Filter:** Schnelle Filterung nach Konfektionsgrösse, Kategorie, Geschlecht und Saison.
- **Einfaches Reservierungssystem:** Interessierte können Kleidungsstücke mit einem Klick reservieren.
- **Bildoptimierung:** Automatische Skalierung und WebP-Komprimierung beim Upload, um Speicherplatz und Bandbreite zu sparen.
---
## 🛠 Technologiestack (Python-basiert)
- **Backend:** Python 3.11+ mit [FastAPI](https://fastapi.tiangolo.com/) (hohe Performance, automatische OpenAPI/Swagger-Dokumentation)
- **Datenbank & ORM:** SQLite mit [SQLAlchemy](https://www.sqlalchemy.org/) & [Alembic](https://alembic.sqlalchemy.org/) (Migrations)
- **Bildverarbeitung:** Pillow (PIL) für WebP-Konvertierung und Resizing
- **Frontend (Optionen):**
- *Variante A (Single File / Monolith):* Jinja2 Templates + HTMX + Tailwind CSS (sehr schnell zu entwickeln, kein Node.js Build-Step nötig)
- *Variante B (Decoupled):* Vue.js / React SPA (greift auf die REST-API zu)
- **Deployment:** Docker & Docker Compose (leicht auf Unraid / Server auszuführen)
---
## 🗄 Datenbank-Schema
### 1. `categories`
| Feld | Typ | Beschreibung |
|---|---|---|
| `id` | INTEGER (PK) | Eindeutige ID |
| `name` | VARCHAR(50) | Name (z. B. Hosen, Jacken, Schuhe, Pullover) |
| `slug` | VARCHAR(50) | URL-freundlicher Name |
### 2. `items` (Haupttabelle)
| Feld | Typ | Beschreibung |
|---|---|---|
| `id` | INTEGER / UUID (PK) | Eindeutige ID |
| `title` | VARCHAR(100) | Kurzer Titel (z. B. "Warme Winterjacke rot") |
| `description` | TEXT | Optionale Details oder Mängel |
| `size` | VARCHAR(20) | Konfektionsgrösse (z. B. `98/104`, `24`, `80`) |
| `gender` | VARCHAR(10) | `boy`, `girl`, `unisex` |
| `season` | VARCHAR(15) | `spring_summer`, `autumn_winter`, `all_year` |
| `condition` | VARCHAR(20) | `new`, `very_good`, `good`, `worn` |
| `status` | VARCHAR(20) | `available`, `reserved`, `given_away` |
| `category_id` | FK -> `categories.id` | Zuordnung zur Kategorie |
| `reserved_by` | VARCHAR(100) | Name/Kontakt der Person, die es reserviert hat |
| `created_at` | TIMESTAMP | Erstellungsdatum |
| `updated_at` | TIMESTAMP | Letztes Update |
### 3. `item_images`
| Feld | Typ | Beschreibung |
|---|---|---|
| `id` | INTEGER / UUID (PK) | Eindeutige ID |
| `item_id` | FK -> `items.id` | Referenz zum Kleidungsstück |
| `image_url` | VARCHAR(255) | Pfad zur Bilddatei (z. B. `/static/uploads/img_1234.webp`) |
| `is_primary` | BOOLEAN | Hauptbild für Galerie-Vorschau |
---
## 🔌 API-Endpunkte (FastAPI REST)
### Kleidung verwalten & durchsuchen
- `GET /api/v1/items`
- **Query-Params:** `?size=98/104&category_id=2&status=available&gender=unisex`
- **Response:** Liste gefilterter Kleidungsstücke mit Primärbild.
- `GET /api/v1/items/{id}`
- **Response:** Einzelnes Kleidungsstück inklusive aller Bilder und Details.
- `POST /api/v1/items`
- **Content-Type:** `multipart/form-data`
- **Body:** JSON-Daten + Bild-Uploads.
- `PATCH /api/v1/items/{id}`
- **Body:** Partial Update für Status, Titel, Beschreibung etc.
- `DELETE /api/v1/items/{id}`
- **Description:** Kleidungsstück und zugehörige Bilder löschen.
### 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`.
### Stammdaten
- `GET /api/v1/categories`
- `GET /api/v1/sizes`
---
## 📂 Empfohlene Projekt-Struktur
```text
kinderkleider-app/
├── app/
├── database.py # DB-Engine & Session Setup
├── models.py # SQLAlchemy Modelle
├── schemas.py # Pydantic Schemas (Request/Response validation)
├── crud.py # Datenbank-Zugriffslogik
├── main.py # FastAPI App Entrypoint & Routen
├── utils.py # Bild-Komprimierung & Helper
│ ├── static/ # Uploaded Images & CSS
│ └── templates/ # Jinja2 Templates (falls HTML direkt gerendert wird)
├── Dockerfile
├── docker-compose.yml
├── requirements.txt
└── README.md
```
---
## 📋 Meilensteine / Roadmap
- [ ] **Phase 1: Backend Basic MVP**
- [ ] FastAPI Setup mit SQLite & SQLAlchemy
- [ ] Datenmodelle & Migrationen anlegen
- [ ] CRUD Endpunkte für `items` und `categories` testen (Swagger UI)
- [ ] **Phase 2: Bild-Handling & Speicher**
- [ ] `multipart/form-data` Upload via FastAPI
- [ ] Pillow-Integration zur Konvertierung aller Uploads nach WebP (max. 1200px Breite)
- [ ] **Phase 3: Frontend Setup**
- [ ] Mobile-First Layout mit Tailwind CSS
- [ ] Filterleiste für Grösse, Kategorie & Status
- [ ] Reservierungs-Modal / Button
- [ ] **Phase 4: Containerisierung & Deployment**
- [ ] `Dockerfile` & `docker-compose.yml` schreiben
- [ ] Persistence per Volume-Mount für Datenbank & Bild-Uploads
- [ ] Gitea Actions: Image bei einem Versions-Tag (`v*`) bauen, testen und in
die Registry stellen — gleiches Vorgehen wie bei der Kantone-App
(`.gitea/workflows/docker-image.yml` dort als Vorlage)
- [ ] Zweite Compose-Datei zum Starten des fertigen Images (ohne Quellcode)