Files
Cellar---Alcools-collection/README.md
T
2026-06-26 11:54:29 +02:00

8.8 KiB

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.

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.