MDP INVARIANTS et Contrat d'architecture
INVARIANTS — MDP (inclut le contrat d’architecture)
Source de vérité. Toute proposition/modification doit respecter ces règles. Les secrets ne doivent jamais apparaître dans les
.envcommittés.
1) Nommage, dépôts, répertoires
- APP_DEPOT : nom du dépôt GitHub (= nom du répertoire en DEV) →
~/projets/${APP_DEPOT}. - APP_SLUG : préfixe stable pour images/containers/volumes/réseau et nom du répertoire en PROD →
/opt/apps/${APP_SLUG}. - APP_ENV ∈
{dev, prod}. - APP_NAME : nom humain de l’application (entêtes/logs/outils).
Contrainte : toutes les applications utilisant ce template réutilisent les mêmes noms de services Compose (db, backend, vite, frontend) et les scripts universels déduisent l’environnement courant et, si nécessaire, APP_SLUG/APP_DEPOT.
2) Ports dérivés de APP_NO (DEV)
À partir de APP_NO = N (ex. N=1) :
- DEV_DB_PORT =
5432 + N→ ex.5433 - DEV_API_PORT =
8001 + N→ ex.8002 - DEV_VITE_PORT =
5173 + N→ ex.5174
Invariants : ces ports sont utilisés dans Compose et par les scripts.
3) Environnements & secrets
Fichiers
.env.dev/.env.prod: variables non sensibles (versionnées).env.dev.local/.env.prod.local: secrets (non versionnés)
Secrets (à conserver uniquement dans *.local)
POSTGRES_PASSWORD,DJANGO_SECRET_KEYADMIN_USERNAME,ADMIN_PASSWORD,ADMIN_EMAIL(création superuser)
Interdit : pas de secrets dans
.env.dev/.env.prod.
4) Conteneurisation (contrat)
Services Compose (noms fixes)
db(Postgres 16-alpine)backend(Django, image${APP_SLUG}-backend:dev|prod)vite(Node 20-alpine, dev-server)frontend(optionnel en dev : build statique via Caddy/Nginx)
Noms concrets
- Containers/volumes/réseau :
${APP_SLUG}_<service>_${APP_ENV}/${APP_SLUG}_<nom>_${APP_ENV} - Réseau par défaut :
${APP_SLUG}_appnet
Dépendances & healthchecks
backenddépend dedb(healthy)vitedépend debackend
5) Backend Django (API)
-
Commande dev :
python manage.py runserver 0.0.0.0:8000 -
Base API : préfixe
/api/(Djangourls.py) -
Auth : SimpleJWT actif (
rest_framework_simplejwt,JWTAuthentication) -
CORS/CSRF (dev) :
CORS_ALLOWED_ORIGINS = http://localhost:${DEV_VITE_PORT}CSRF_TRUSTED_ORIGINS = http://localhost, http://127.0.0.1, http://localhost:${DEV_VITE_PORT}[, http://localhost:${DEV_API_PORT}]
-
ALLOWED_HOSTS (dev minimal) :
localhost,127.0.0.1,0.0.0.0,<containers backend/frontend> -
URLs JWT :
/api/auth/jwt/create/(+ refresh/verify si exposés)
6) Frontend (Vite + React + Axios)
Vite (dev) — frontend/vite.config.js
export default defineConfig({
plugins: [react()],
server: {
host: true,
port: 5173, // mappé vers 5173+N côté hôte
proxy: { '/api': { target: 'http://backend:8000', changeOrigin: false } },
},
})
Variables front
VITE_API_BASE=/api(toujours un chemin relatif en dev & prod)- Injectée via Compose dans le service
vite
Axios & base URL — frontend/src/api.js (points clés)
import axios from "axios"export const BASE = normalizeBase(import.meta.env.VITE_API_BASE)export const api = axios.create({ baseURL: BASE })- POST login :
api.post('auth/jwt/create/', { username, password }) - Intercepteur 401 → purge Authorization & redirection
/login
Stockage tokens (login)
- Source unique :
localStorage.setItem('mdp.jwt', JSON.stringify({ access, refresh })) - Compat rétro (optionnel) :
localStorage.setItem('token', access) - Après login :
setAccessToken(access)(headerAuthorization: Bearer …) - Logout :
removeItem('mdp.jwt')(+ purgetokensi présent)
7) Flux d’exécution (dev)
viteécoute5173(conteneur) →5173+N(hôte)- Le navigateur appelle
/api/...→ Vite proxy vershttp://backend:8000 - Django répond ; CORS/CSRF/ALLOWED_HOSTS sont alignés sur l’origine dev
8) Compose (dev) — points d’attention
env_file: .env.devet.env.dev.localpourdb,backend,vitebackend.ports: "${DEV_API_PORT}:8000"vite.ports: "${DEV_VITE_PORT}:5173"VITE_API_BASE: "/api"injecté dansvite
9) Vérification automatique des invariants
Script : scripts/verifier-invariants.sh
- Vérifie Vite proxy (
/api, targetbackend:8000,changeOrigin:false) - Vérifie
api.js(import axios,import.meta.env.VITE_API_BASE,axios.create, endpoint JWT) - Vérifie Django (SimpleJWT, URLs,
runserver 0.0.0.0:8000) - Vérifie Compose (commande runserver &
VITE_API_BASE:"/api") - Smoke tests : création JWT via
http://localhost:${DEV_VITE_PORT}/api/..., et lecture d’un endpoint protégé (par défaut/api/passwords/)
Toute PR doit passer ce script en local avant push.
10) Production (survol)
- Variables dans
.env.prod+ secrets dans.env.prod.local. - Arborescence PROD : code déployé sous
/opt/apps/${APP_SLUG}. VITE_API_BASEreste/api(chemin relatif).- Reverse proxy (Apache/Nginx/Caddy) doit publier
/api→ backend (gunicorn/uwsgi) et les assets du front. ALLOWED_HOSTSminimal :APP_HOSTet éventuels alias.
11) Règles Do / Don’t
Do
- Garder
/apirelatif côté front (pas d’URL absolue) - Conserver les noms de services Compose stables (
db,backend,vite,frontend) - Centraliser les secrets dans
*.local(jamais commit) - Utiliser
mdp.jwtcomme source unique pour les tokens - Respecter les chemins de travail :
~/projets/${APP_DEPOT}(dev) et/opt/apps/${APP_SLUG}(prod)
Don’t
- Pas de secrets dans
.env.dev/.env.prod(ni dans la doc, ni dans les exemples) - Pas de
changeOrigin:trueen dev pour/api - Pas d’URL API codée en dur dans le front (toujours
VITE_API_BASE)
12) Checklist de revue (avant merge)
-
scripts/verifier-invariants.shOK -
frontend/vite.config.jsconforme -
frontend/src/api.jsconforme (BASE, axios, login endpoint, interceptor) - Backend lance
runserver 0.0.0.0:8000en dev -
CORS_ALLOWED_ORIGINScontienthttp://localhost:${DEV_VITE_PORT} -
CSRF_TRUSTED_ORIGINSlistées (localhost/127.0.0.1/ports dev) -
ALLOWED_HOSTSminimal -
.env.*.localprésents en local (non commit) - Chemins de travail conformes (dev :
~/projets/${APP_DEPOT}, prod :/opt/apps/${APP_SLUG})
13) Emplacement du document
- Chemin recommandé :
docs/INVARIANTS.md(ce document). Lien depuisREADME.md.
14) Extension cross-apps
Ce document sert de template pour d’autres applications : mêmes noms de services,
mêmes conventions d’API/auth, dérivation des ports via APP_NO, mêmes scripts de vérification.
Seules changent les valeurs d’APP_* et le métier.
Dernière mise à jour : synchronisée avec l’état du projet (tree -L 4 -I 'node_modules|dist').