211 lines
9.7 KiB
Markdown
211 lines
9.7 KiB
Markdown
# Template Django + React + MySQL
|
||
|
||
Un squelette réutilisable inspiré de l'organisation de GoodDrive, **sans ses fonctionnalités** :
|
||
Django REST Framework, React/Vite, Tailwind 4, composants shadcn/ui, Oxlint, MySQL 8 et Docker Compose.
|
||
|
||
## Démarrage rapide
|
||
|
||
1. Compléter les secrets du `.env` (ou les générer avec `python scripts/init_env.py`).
|
||
2. Modifier si nécessaire `FRONTEND_PORT`, `BACKEND_PORT`, `ALLOWED_HOSTS` et les origines autorisées.
|
||
3. Démarrer avec le script principal :
|
||
|
||
```sh
|
||
chmod +x start.sh
|
||
./start.sh
|
||
```
|
||
|
||
`start.sh` initialise le `.env`, puis détecte Docker Compose. Si Docker est disponible,
|
||
il construit et démarre les trois services. Sinon, il lance **Django et React dans le même
|
||
environnement** (mode Pterodactyl, avec une base MySQL externe déjà accessible).
|
||
|
||
Tu peux choisir explicitement :
|
||
|
||
```sh
|
||
./start.sh --compose # Docker Compose
|
||
./start.sh --standalone # Un seul environnement, sans Docker
|
||
```
|
||
|
||
En mode standalone, Python 3, Node.js/npm et les bibliothèques nécessaires à `mysqlclient`
|
||
doivent être disponibles. Le script installe les dépendances Python manquantes, installe
|
||
les dépendances React et compile le frontend avant de lancer les deux serveurs.
|
||
La variable `MYSQL_HOST=db` du modèle fonctionne **uniquement en mode Compose** :
|
||
en mode standalone, indique l'adresse réelle de ton serveur MySQL dans `.env`.
|
||
|
||
Le mode standalone utilise `vite preview` pour servir le build et le proxy API ; pour
|
||
un site public à forte charge, préfère le mode Docker Compose avec Nginx.
|
||
|
||
Le script ne télécharge ni n'écrase automatiquement le dépôt Git.
|
||
|
||
|
||
- **Frontend** : http://localhost:1028 (ou `FRONTEND_PORT`)
|
||
- **Backend** : http://localhost:1029/api/health/ (ou `BACKEND_PORT`)
|
||
- **Administration React (shadcn/ui)** : http://localhost:1028/admin/
|
||
- **Admin Django technique** : http://localhost:1028/django-admin/
|
||
|
||
Au premier lancement, `ensure_admin` crée le compte à partir de `ADMIN_*`. Si le compte existe déjà, il **ne change pas son mot de passe** à chaque redémarrage.
|
||
|
||
**Attention :** ne versionne jamais `.env`, et remplace les noms/mots de passe par des valeurs propres à chaque projet. `DEBUG=True` ne doit pas être utilisé en production.
|
||
|
||
## Structure
|
||
|
||
```text
|
||
.
|
||
├── .env # paramètres locaux, non versionné
|
||
├── .env.example # modèle sans secrets
|
||
├── docker-compose.yml
|
||
├── start.sh # Démarrage Compose ou standalone / Pterodactyl
|
||
├── scripts/
|
||
│ └── init_env.py
|
||
├── backend/
|
||
│ ├── Dockerfile
|
||
│ ├── entrypoint.sh
|
||
│ ├── requirements.txt
|
||
│ ├── manage.py
|
||
│ ├── core/
|
||
│ │ ├── settings.py
|
||
│ │ ├── origins.py
|
||
│ │ └── urls.py
|
||
│ └── common/
|
||
│ ├── urls.py
|
||
│ ├── views.py
|
||
│ ├── admin_api.py # API sécurisée pour le tableau de bord React
|
||
│ └── management/commands/ensure_admin.py
|
||
└── frontend/
|
||
├── Dockerfile
|
||
├── nginx.conf
|
||
├── package.json
|
||
├── .oxlintrc.json # Oxlint : JS/JSX, React et accessibilité
|
||
├── components.json
|
||
├── vite.config.js
|
||
└── src/
|
||
├── App.jsx # sélection des pages
|
||
├── index.css
|
||
├── pages/ # HomePage, AdminPage
|
||
├── hooks/ # logique React et états réutilisables
|
||
├── services/ # appels HTTP centralisés, AUCUN fetch dans JSX
|
||
│ ├── apiClient.js
|
||
│ ├── healthService.js
|
||
│ ├── authService.js
|
||
│ └── adminService.js
|
||
└── components/
|
||
├── admin/ # widgets de la page d'administration
|
||
├── layout/ # composants partagés
|
||
└── ui/ # composants shadcn/ui
|
||
```
|
||
|
||
## Origines CORS/CSRF et ports automatiques
|
||
|
||
`backend/core/settings.py` transforme les origines du `.env` automatiquement.
|
||
|
||
```ini
|
||
FRONTEND_PORT=1028
|
||
BACKEND_PORT=1029
|
||
CORS_ALLOWED_ORIGINS=http://localhost,http://127.0.0.1
|
||
CSRF_TRUSTED_ORIGINS=http://localhost,http://127.0.0.1
|
||
```
|
||
|
||
Valeurs effectives :
|
||
|
||
```python
|
||
CORS_ALLOWED_ORIGINS = ["http://localhost:1028", "http://127.0.0.1:1028"]
|
||
CSRF_TRUSTED_ORIGINS = [
|
||
"http://localhost:1029", "http://127.0.0.1:1029", # backend
|
||
"http://localhost:1028", "http://127.0.0.1:1028", # frontend
|
||
]
|
||
```
|
||
|
||
Les origines comportant **déjà un port explicite** sont conservées telles quelles :
|
||
`http://localhost:3000` reste `http://localhost:3000`.
|
||
|
||
Pour un reverse proxy HTTPS avec ports standards 443, mets `AUTO_PORT_CORS=False` et
|
||
`AUTO_PORT_CSRF=False`, puis indique tes origines complètes dans les deux variables.
|
||
Cela évite d'ajouter `:1028` à `https://mon-domaine.fr`.
|
||
|
||
**Pourquoi le frontend figure également dans CSRF ?** Django en a besoin pour
|
||
les formulaires avec cookie/session provenant de l'interface. Cela complète
|
||
la règle demandée « port backend pour CSRF, port frontend pour CORS ».
|
||
|
||
Dans ce template, le frontend utilise `/api/` sur le **même domaine** grâce au proxy Nginx :
|
||
CORS n'est normalement pas nécessaire pour la communication interne, mais reste
|
||
configuré si une autre application appelle directement l'API exposée sur le port backend.
|
||
|
||
## Architecture Docker
|
||
|
||
- `db` : MySQL 8.4 avec volume `db_data` (non exposé sur le réseau hôte).
|
||
- `backend` : Django + Gunicorn sur le port interne 8000, publié sur `BACKEND_PORT`.
|
||
- `frontend` : React compilé puis servi par Nginx sur le port interne 80, publié sur `FRONTEND_PORT`.
|
||
- Nginx relaie `/api/`, `/django-admin/` et `/static/` vers Django. `/admin/` est une page React.
|
||
|
||
Le port `MYSQL_PORT` est **le port de MySQL dans le réseau des conteneurs** (3306 avec le service `db`). Pour une base distante, adapte `MYSQL_HOST` et `MYSQL_PORT`. Le service `db` reste créé par Compose dans cette version du template.
|
||
|
||
## Ajouter tes fonctionnalités
|
||
|
||
- Django : nouvelle application via `python manage.py startapp monapp` ; l'ajouter dans `INSTALLED_APPS` et ses URL dans `core/urls.py`.
|
||
- React : nouvelles pages dans `frontend/src/pages/`, hooks dans `hooks/` et appels à l’API dans `services/`. Ne pas appeler `fetch` dans les composants JSX.
|
||
- shadcn/ui : le fichier `components.json` est présent. Ajouter les composants via la CLI shadcn (`npx shadcn@latest add dialog`, par exemple). Le template inclut Button, Card, Input, Label, Table, Badge, Dialog, Separator et Alert.
|
||
- Variables supplémentaires : les ajouter au `.env.example` et les lire via `decouple.config` côté Django.
|
||
|
||
## Qualité du code avec Oxlint
|
||
|
||
Oxlint est inclus dans les dépendances de développement du frontend. Il contrôle le JavaScript/JSX, les règles React, les erreurs fréquentes et certaines règles d’accessibilité. Il ne remplace pas un formateur de code.
|
||
|
||
```sh
|
||
cd frontend
|
||
npm install
|
||
npm run lint # Contrôle sans modifier les fichiers
|
||
npm run lint:fix # Applique les corrections automatiques sûres disponibles
|
||
```
|
||
|
||
La configuration se trouve dans `frontend/.oxlintrc.json`. Les dossiers `dist` et `coverage` sont ignorés. Oxlint est volontairement **indépendant du démarrage** : `start.sh` ne bloque pas le lancement du site sur un avertissement de lint.
|
||
|
||
## Vérifier et exploiter
|
||
|
||
```sh
|
||
# Logs
|
||
docker compose logs -f backend
|
||
docker compose logs -f frontend
|
||
|
||
# Voir les origines effectives (ne pas afficher le SECRET_KEY)
|
||
docker compose exec backend python manage.py shell -c "from django.conf import settings; print(settings.CORS_ALLOWED_ORIGINS, settings.CSRF_TRUSTED_ORIGINS)"
|
||
|
||
# Tests unitaires des origines (sans Django)
|
||
cd backend && python -m unittest discover -s tests
|
||
# Avec une base Django de test :
|
||
python manage.py test common
|
||
|
||
# Stopper sans détruire MySQL
|
||
docker compose down
|
||
```
|
||
|
||
**Ne pas utiliser `docker compose down -v`** : cela supprime le volume de la base.
|
||
|
||
## Notes de sécurité
|
||
|
||
- `SECRET_KEY`, `MYSQL_PASSWORD`, `MYSQL_ROOT_PASSWORD` et `ADMIN_PASSWORD` sont générés localement par le script, sans être publiés dans le ZIP.
|
||
- Les identifiants MySQL ne sont pas intégrés dans les images Docker.
|
||
- La route de diagnostic `/api/health/` est volontairement publique ; les futures routes API sont protégées par défaut dans DRF.
|
||
- Sur Internet, configure HTTPS, `ALLOWED_HOSTS`, origines, cookies sécurisés et reverse proxy avant mise en ligne.
|
||
- En production, évite d'exposer directement le port backend si les appels passent seulement par Nginx.
|
||
|
||
## Tableau de bord administrateur (shadcn/ui)
|
||
|
||
- `/admin/` : interface React avec connexion par session Django, thème clair/sombre,
|
||
statistiques, recherche, création de comptes et activation/désactivation.
|
||
- `/django-admin/` : administration Django native conservée pour la gestion avancée.
|
||
- Seuls les comptes `is_staff` accèdent au tableau de bord. Pour **créer ou désactiver**
|
||
des comptes, il faut en plus être **superutilisateur**. Le compte connecté et les
|
||
autres superutilisateurs sont protégés contre la désactivation.
|
||
- L'API se trouve dans `backend/common/admin_api.py`. La session Django et les jetons
|
||
CSRF protègent les actions, **les secrets ne sont jamais stockés dans le frontend**.
|
||
- Le frontend parle au backend par un chemin relatif `/api/`, proxifié en Compose
|
||
comme en standalone. `frontend/src/services/apiClient.js` centralise les erreurs,
|
||
cookies et en-têtes CSRF.
|
||
- L'interface ne remplace pas les permissions fines de Django Admin pour les modèles
|
||
que tu ajouteras : la page React sert de base claire et extensible.
|
||
|
||
## Validation
|
||
|
||
Le template inclut les tests `backend/common/tests.py` pour l'authentification,
|
||
les autorisations administrateur et la CSRF. Pour les lancer, une base Django de test
|
||
accessible est requise : `python manage.py test common`.
|