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

5.8 KiB
Raw Blame History

📦 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 (hohe Performance, automatische OpenAPI/Swagger-Dokumentation)
  • Datenbank & ORM: SQLite mit SQLAlchemy & Alembic (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

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)