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

229 lines
8.8 KiB
Markdown

# 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](https://podman.io/) (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
```bash
# 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 :
```bash
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
```bash
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
```bash
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.