2026-06-26 12:31:45 +02:00
2026-06-26 11:54:29 +02:00
2026-06-26 11:59:52 +02:00
2026-06-26 11:54:29 +02:00
2026-06-26 11:54:29 +02:00
2026-06-26 12:31:45 +02:00
2026-06-26 11:54:29 +02:00
2026-06-26 09:52:01 +00:00
2026-06-26 11:59:52 +02:00

Cellar — Collection de vins, bières et spiritueux

Application web fullstack pour gérer une collection personnelle de boissons. Interface dark theme moderne, authentification JWT, upload de photos.

Cellar - Interface

Stack technique

Composant Technologie
Frontend React 18, TypeScript, Vite, Tailwind CSS
Backend Python 3.12, FastAPI, SQLAlchemy, SQLite
Auth JWT (python-jose), bcrypt, token versioning
Infra Podman/Docker, nginx, multi-stage builds

Prérequis

  • Podman (ou Docker) + podman-compose (ou docker-compose)

Node.js et Python ne sont pas nécessaires — tout est géré dans les containers.

Démarrage rapide

# Cloner et configurer
cp .env.example .env
# Éditer .env avec un SECRET_KEY aléatoire et un ADMIN_PASSWORD fort

# Build et lancement
podman-compose up --build

L'app est accessible sur http://localhost:8080.

Configuration (.env)

Variable Description Défaut
SECRET_KEY Clé secrète pour signer les JWT (64+ caractères) Requis — le backend refuse de démarrer sans
ADMIN_PASSWORD Mot de passe du compte admin Généré aléatoirement si non défini
CORS_ORIGINS Origines autorisées pour CORS http://localhost:5173,http://localhost:3000
BUILDAH_FORMAT Format OCI pour Buildah/Podman docker

Important : Générez un SECRET_KEY aléatoire :

python3 -c "import secrets; print(secrets.token_hex(32))"

Compte admin

Au premier démarrage, un compte admin est créé automatiquement. Le mot de passe est soit ADMIN_PASSWORD (depuis .env), soit un mot de passe aléatoire généré automatiquement.

Structure du projet

wine/
├── backend/
│   ├── main.py              # Point d'entrée FastAPI, startup, CORS
│   ├── auth.py              # JWT, hashage mot de passe, get_current_user
│   ├── database.py          # Engine SQLAlchemy, migration, session
│   ├── models.py            # Modèles: User, Drink, Invitation
│   ├── schemas.py           # Schémas Pydantic (validation)
│   ├── ratelimit.py         # Rate limiter in-memory
│   ├── requirements.txt     # Dépendances Python (pinnées)
│   ├── requirements-dev.txt # Dépendances dev (pytest, httpx)
│   ├── tests/               # Tests pytest
│   ├── Dockerfile           # Build multi-stage (Python 3.12-slim)
│   └── routers/
│       ├── auth.py          # Login, register, logout, invitations
│       ├── drinks.py        # CRUD boissons + upload images
│       └── admin.py         # Gestion utilisateurs (admin only)
├── frontend/
│   ├── src/
│   │   ├── api/             # Client API, types TypeScript
│   │   ├── auth/            # Contexte auth, ProtectedRoute
│   │   ├── components/      # DrinkCard, RatingStars, Layout
│   │   └── pages/           # Home, Login, Register, Admin, etc.
│   ├── nginx.conf           # Config nginx (rate limiting, CSP, proxy)
│   ├── nginx-main.conf      # Override nginx.conf (user root pour podman)
│   ├── Dockerfile           # Build multi-stage (Node → nginx)
│   └── package.json         # Dépendances JS
├── docker-compose.yml       # Orchestration backend + frontend
├── .env                     # Variables d'environnement
└── .env.example             # Template pour .env

Dépendances backend (Python)

Package Version Usage
fastapi * Framework web
uvicorn[standard] * Serveur ASGI
sqlalchemy * ORM, accès SQLite
pydantic * Validation des données
python-multipart * Upload de fichiers
Pillow * Vérification images uploadées
aiofiles * Async file I/O
python-jose[cryptography] * JWT encoding/decoding
bcrypt * Hashage des mots de passe

Dépendances frontend (Node)

Package Version Usage
react ^18.3.1 UI framework
react-dom ^18.3.1 React DOM
react-router-dom ^7.1.1 Routing SPA
typescript ^5.6.3 Typage statique
vite ^6.0.3 Bundler
tailwindcss ^3.4.16 CSS utility-first
@vitejs/plugin-react ^4.3.4 Plugin React pour Vite

Dev local (sans Docker)

Si tu veux développer sans container, tu auras besoin de Python 3.12+ et Node.js 20+.

Backend

cd backend
python -m venv venv && source venv/bin/activate
pip install -r requirements.txt
# Créer un .env ou exporter les variables :
export SECRET_KEY=$(python3 -c "import secrets; print(secrets.token_hex(32))")
export ADMIN_PASSWORD=monpassword
uvicorn main:app --reload --port 8000

Frontend

cd frontend
npm install
npm run dev
# Accessible sur http://localhost:5173

API Endpoints

Auth

Méthode Route Description Auth
POST /api/auth/login Connexion Non
POST /api/auth/register Inscription (invitation requise) Non
GET /api/auth/me Profil utilisateur Oui
POST /api/auth/change-password Changer mot de passe Oui
POST /api/auth/logout Déconnexion (invalide le token) Oui
POST /api/auth/invitations Créer une invitation Admin
GET /api/auth/invitations Lister les invitations Admin

Drinks

Méthode Route Description Auth
GET /api/drinks Lister les boissons Oui
POST /api/drinks Ajouter une boisson Oui
GET /api/drinks/{id} Détail d'une boisson Oui
PUT /api/drinks/{id} Modifier une boisson Oui
DELETE /api/drinks/{id} Supprimer une boisson Oui
POST /api/drinks/{id}/upload-image Upload d'image Oui
GET /api/drinks/{id}/image?token=... Voir l'image Oui (via query)

Admin

Méthode Route Description Auth
GET /api/admin/users Lister les utilisateurs Admin
DELETE /api/admin/users/{id} Supprimer un utilisateur (+ données) Admin
POST /api/admin/users/{id}/reset-password Reset mot de passe d'un user Admin
POST /api/admin/users/{id}/toggle-admin Promouvoir/rétrograder admin Admin
GET /api/admin/stats Statistiques Admin

Sécurité

Authentification

  • JWT avec expiration 24h
  • Token versioning : le logout, le changement de mot de passe et le reset admin invalident tous les tokens existants
  • Tokens stockés en sessionStorage (pas de localStorage)
  • Mot de passe : bcrypt avec cost factor par défaut
  • SECRET_KEY vérifié au démarrage — le backend refuse de démarrer si manquant ou placeholder
  • Messages d'erreur génériques (pas d'énumération username/email)

Rate Limiting

  • Login : 5 requêtes/min par IP
  • Register : 3 requêtes/5min par IP
  • Change-password : 5 requêtes/min par IP
  • Basé sur X-Real-IP (défini par nginx, non-spoofable), fallback X-Forwarded-For
  • Compteur incrémenté avant l'exécution (les échecs comptent aussi)
  • Protection nginx en supplément

Uploads

  • Vérification MIME (magic bytes via Pillow + vérification du format réel)
  • Taille max 10 MB
  • Filenames UUID (pas de path traversal)
  • Endpoint image protégé par authentification (JWT via query param ?token=... pour compatibilité <img>)
  • Vérification que le répertoire de destination est bien dans uploads/

Gestion admin des utilisateurs

  • L'admin peut reset le mot de passe de n'importe quel user (invalide ses tokens, min 8 caractères)
  • L'admin peut promouvoir/rétrograder un user en admin
  • L'admin ne peut pas se modifier lui-même (protection auto-destructrice)
  • Suppression d'un user : boissons + images + invitations associées

Headers sécurité (nginx)

  • X-Content-Type-Options: nosniff
  • X-Frame-Options: DENY
  • Content-Security-Policy strict
  • Referrer-Policy: strict-origin-when-cross-origin
  • Permissions-Policy restrictive

Docker

  • Container non-root (appuser)
  • cap_drop: ALL + no-new-privileges
  • 1 seul worker uvicorn (cohérence SQLite + rate limiter)
  • Limites ressources (512MB backend, 128MB frontend)
  • Logs avec rotation
  • Healthchecks pour orchestration

Fonctionnalités

  • Gestion de vins, bières et spiritueux avec champs spécifiques par catégorie
  • Notation par étoiles (0-5)
  • Recherche full-text (nom, région, producteur, etc.)
  • Filtres par catégorie et note minimale
  • Upload de photos avec validation
  • Édition des boissons existantes (tous les champs modifiables)
  • Interface responsive dark theme
  • Panel admin : liste, suppression, reset password, toggle admin, statistiques
  • Système d'invitation pour l'inscription (lien copiable avec clipboard fallback)

Licence

Projet personnel.

S
Description
No description provided
Readme MIT 18 MiB
Languages
TypeScript 64%
Python 32.9%
CSS 1.2%
Dockerfile 0.8%
JavaScript 0.6%
Other 0.5%