Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c83fd889b8 | ||
|
|
cf22b2ae55 | ||
|
|
16a0cc4d3b | ||
|
|
5e7cb005ac |
@@ -1,18 +1,36 @@
|
|||||||
BACKEND := apps/backend
|
BACKEND := apps/backend
|
||||||
|
FRONTEND := apps/frontend
|
||||||
|
|
||||||
.DEFAULT_GOAL := help
|
.DEFAULT_GOAL := help
|
||||||
.PHONY: help install dev lint format typecheck test test-cov test-integration check \
|
.PHONY: help install install-backend install-frontend dev dev-backend dev-frontend \
|
||||||
|
lint format typecheck test test-cov test-integration check \
|
||||||
openapi docker-build db-up db-down db-reset db-logs db-psql migrate bootstrap-admin
|
openapi docker-build db-up db-down db-reset db-logs db-psql migrate bootstrap-admin
|
||||||
|
|
||||||
help: ## Liste les cibles disponibles
|
help: ## Liste les cibles disponibles
|
||||||
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-16s\033[0m %s\n", $$1, $$2}'
|
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-16s\033[0m %s\n", $$1, $$2}'
|
||||||
|
|
||||||
install: ## Installe les dépendances du backend
|
install: install-backend install-frontend ## Installe les dépendances backend et frontend
|
||||||
|
|
||||||
|
install-backend: ## Installe les dépendances du backend
|
||||||
cd $(BACKEND) && uv sync --all-groups
|
cd $(BACKEND) && uv sync --all-groups
|
||||||
|
|
||||||
dev: ## Lance l'API en rechargement à chaud
|
install-frontend: ## Installe les dépendances du frontend
|
||||||
|
cd $(FRONTEND) && npm ci
|
||||||
|
|
||||||
|
dev: ## Lance toute la stack (backend + frontend) en rechargement à chaud
|
||||||
|
@trap 'kill 0' EXIT INT TERM; \
|
||||||
|
$(MAKE) --no-print-directory dev-backend & \
|
||||||
|
$(MAKE) --no-print-directory dev-frontend & \
|
||||||
|
wait
|
||||||
|
|
||||||
|
dev-backend: ## Lance l'API seule en rechargement à chaud
|
||||||
|
@echo "backend -> http://localhost:8000 (docs sur /docs)"
|
||||||
cd $(BACKEND) && uv run uvicorn app.main:create_app --factory --reload --host 0.0.0.0 --port 8000
|
cd $(BACKEND) && uv run uvicorn app.main:create_app --factory --reload --host 0.0.0.0 --port 8000
|
||||||
|
|
||||||
|
dev-frontend: ## Lance le frontend seul en rechargement à chaud
|
||||||
|
@echo "frontend -> http://localhost:4200"
|
||||||
|
cd $(FRONTEND) && npm start
|
||||||
|
|
||||||
lint: ## Analyse statique du backend
|
lint: ## Analyse statique du backend
|
||||||
cd $(BACKEND) && uv run ruff check .
|
cd $(BACKEND) && uv run ruff check .
|
||||||
|
|
||||||
|
|||||||
@@ -63,16 +63,17 @@ L'etat detaille de chaque brique et les vues d'architecture sont dans
|
|||||||
|
|
||||||
## Demarrage
|
## Demarrage
|
||||||
|
|
||||||
Prerequis : uv, Docker. Le poste doit disposer de Python 3.14, que `uv` installe seul.
|
Prerequis : uv, Docker, Node 24 LTS (npm fourni). Le poste doit disposer de Python 3.14, que
|
||||||
|
`uv` installe seul.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cp .env.example .env # variables de docker-compose
|
cp .env.example .env # variables de docker-compose
|
||||||
cp apps/backend/.env.example apps/backend/.env # variables du backend hors conteneur
|
cp apps/backend/.env.example apps/backend/.env # variables du backend hors conteneur
|
||||||
|
|
||||||
make db-up # PostgreSQL + TimescaleDB, publie sur le port 5433
|
make db-up # PostgreSQL + TimescaleDB, publie sur le port 5433
|
||||||
make install # dependances du backend
|
make install # dependances du backend et du frontend
|
||||||
make migrate # applique les migrations Alembic
|
make migrate # applique les migrations Alembic
|
||||||
make dev # API sur http://localhost:8000, docs sur /docs
|
make dev # backend sur http://localhost:8000 (docs sur /docs), frontend sur http://localhost:4200
|
||||||
make check # lint + typage + tests
|
make check # lint + typage + tests
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -83,9 +84,11 @@ Deux fichiers d'environnement, deux usages : `.env` a la racine alimente `docker
|
|||||||
5432, souvent deja pris par une autre base.
|
5432, souvent deja pris par une autre base.
|
||||||
|
|
||||||
La boucle de developpement est `make db-up` puis `make dev` : seule la base tourne en
|
La boucle de developpement est `make db-up` puis `make dev` : seule la base tourne en
|
||||||
conteneur. Le service `backend` du `docker-compose.yml` sert la stack complete et la recette,
|
conteneur, le backend et le frontend tournent tous les deux sur le poste, lances ensemble par
|
||||||
et n'embarque pas le source, donc toute modification y demande un
|
`make dev` (logs entrelaces dans le meme terminal, Ctrl+C arrete les deux). `make dev-backend`
|
||||||
`docker compose up -d --build backend`.
|
et `make dev-frontend` restent disponibles pour lancer un seul des deux. Le service `backend`
|
||||||
|
du `docker-compose.yml` sert la stack complete et la recette, et n'embarque pas le source, donc
|
||||||
|
toute modification y demande un `docker compose up -d --build backend`.
|
||||||
|
|
||||||
Verifier que la base repond et que l'extension est chargee :
|
Verifier que la base repond et que l'extension est chargee :
|
||||||
|
|
||||||
|
|||||||
@@ -22,17 +22,6 @@ ORIGINE_VERIFIEE = {
|
|||||||
("POST", "/api/v1/auth/password"),
|
("POST", "/api/v1/auth/password"),
|
||||||
}
|
}
|
||||||
|
|
||||||
# Toute route derrière `require_role` (LecteurDep, OperateurDep, AdminDep) peut rendre 403 pour
|
|
||||||
# `password_change_required`, pas seulement les routes `admin`.
|
|
||||||
ROUTES_A_ROLE = {
|
|
||||||
("GET", "/api/v1/users"),
|
|
||||||
("POST", "/api/v1/users"),
|
|
||||||
("PATCH", "/api/v1/users/{id}"),
|
|
||||||
("POST", "/api/v1/users/{id}/password-reset"),
|
|
||||||
("GET", "/api/v1/sites"),
|
|
||||||
("GET", "/api/v1/sites/{site_id}"),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="module")
|
@pytest.fixture(scope="module")
|
||||||
def schema() -> dict[str, Any]:
|
def schema() -> dict[str, Any]:
|
||||||
@@ -66,11 +55,11 @@ def test_every_route_demanding_an_identity_says_how_it_refuses(schema: dict[str,
|
|||||||
assert muettes == []
|
assert muettes == []
|
||||||
|
|
||||||
|
|
||||||
def test_every_role_guarded_route_documents_the_role_refusal(schema: dict[str, Any]) -> None:
|
def test_every_administration_route_documents_the_role_refusal(schema: dict[str, Any]) -> None:
|
||||||
sans_403 = [
|
sans_403 = [
|
||||||
(methode, chemin)
|
(methode, chemin)
|
||||||
for methode, chemin, operation in operations(schema)
|
for methode, chemin, operation in operations(schema)
|
||||||
if (methode, chemin) in ROUTES_A_ROLE and "403" not in operation["responses"]
|
if "users" in operation.get("tags", []) and "403" not in operation["responses"]
|
||||||
]
|
]
|
||||||
|
|
||||||
assert sans_403 == []
|
assert sans_403 == []
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||||
"version": 1,
|
"version": 1,
|
||||||
"cli": {
|
"cli": {
|
||||||
"packageManager": "npm"
|
"packageManager": "npm",
|
||||||
|
"analytics": false
|
||||||
},
|
},
|
||||||
"newProjectRoot": "projects",
|
"newProjectRoot": "projects",
|
||||||
"projects": {
|
"projects": {
|
||||||
|
|||||||
@@ -35,9 +35,10 @@ flowchart TB
|
|||||||
| `backend` | Construite depuis `apps/backend` | `depends_on: db, condition: service_healthy`. **N'embarque pas le source** : toute modification impose `docker compose up -d --build backend` |
|
| `backend` | Construite depuis `apps/backend` | `depends_on: db, condition: service_healthy`. **N'embarque pas le source** : toute modification impose `docker compose up -d --build backend` |
|
||||||
|
|
||||||
**La boucle de développement n'utilise pas le service `backend`.** `make db-up` puis `make dev` :
|
**La boucle de développement n'utilise pas le service `backend`.** `make db-up` puis `make dev` :
|
||||||
seule la base tourne en conteneur, l'API tourne sur le poste avec le rechargement à chaud. Le
|
seule la base tourne en conteneur, l'API et `ng serve` tournent sur le poste avec le rechargement
|
||||||
service `backend` sert la stack complète et la recette. Les deux occupent le port 8000, ils ne se
|
à chaud, lancés ensemble par `make dev` (`make dev-backend`/`make dev-frontend` pour lancer l'un
|
||||||
lancent donc pas ensemble.
|
des deux seul). Le service `backend` sert la stack complète et la recette. Les deux occupent le
|
||||||
|
port 8000, ils ne se lancent donc pas ensemble.
|
||||||
|
|
||||||
Deux pièges sont documentés en tête du `docker-compose.yml`, ils ne se devinent pas :
|
Deux pièges sont documentés en tête du `docker-compose.yml`, ils ne se devinent pas :
|
||||||
|
|
||||||
|
|||||||
@@ -235,20 +235,6 @@ Les modèles de `app/schemas/errors.py` décrivent ce que les gestionnaires renv
|
|||||||
`loc` n'apparaît dans aucune réponse de cette API : `validation_error_handler()` rend `champ` et
|
`loc` n'apparaît dans aucune réponse de cette API : `validation_error_handler()` rend `champ` et
|
||||||
`type`. Renommer un champ là-bas sans le faire ici rend la documentation fausse en silence.
|
`type`. Renommer un champ là-bas sans le faire ici rend la documentation fausse en silence.
|
||||||
|
|
||||||
**Ajouter une route métier** (`sites` est le gabarit, `reading`/`dataset`/`prediction`/`alert`/
|
|
||||||
`recommendation` suivront) :
|
|
||||||
|
|
||||||
1. Composer ses `responses=` depuis `app/api/openapi.py` : `REPONSES_LECTEUR` ou `REPONSES_ADMIN`
|
|
||||||
au niveau de l'`include_router` dans `app/api/v1/router.py` (401 et le 403 propre au rôle),
|
|
||||||
`REPONSE_VALIDATION` et les codes locaux (404, 409, ...) sur l'endpoint lui-même s'il a un
|
|
||||||
corps, un paramètre ou peut échouer par identifiant.
|
|
||||||
2. Décrire son tag dans `TAGS` (`app/api/openapi.py`).
|
|
||||||
3. Si elle passe par `require_role`, l'ajouter à `ROUTES_A_ROLE`
|
|
||||||
(`tests/api/test_openapi.py`) ; si elle passe par `require_trusted_origin`, l'ajouter à
|
|
||||||
`ORIGINE_VERIFIEE`. Ces deux listes sont maintenues à la main, pas déduites automatiquement du
|
|
||||||
code : une route protégée qui n'y figure pas ne sera pas détectée par les tests.
|
|
||||||
4. `make openapi`, puis `pytest tests/api/test_openapi.py`.
|
|
||||||
|
|
||||||
## Sécurité
|
## Sécurité
|
||||||
|
|
||||||
Voir la vue consolidée dans [00-vue-ensemble.md](00-vue-ensemble.md) et les décisions dans les
|
Voir la vue consolidée dans [00-vue-ensemble.md](00-vue-ensemble.md) et les décisions dans les
|
||||||
|
|||||||
@@ -108,8 +108,9 @@ déploiement, en même temps que sera tranchée la question de l'ingress dans
|
|||||||
le message d'erreur arrive avant toute compilation. Un poste en 22.21 ou en 24.12 ne peut donc ni
|
le message d'erreur arrive avant toute compilation. Un poste en 22.21 ou en 24.12 ne peut donc ni
|
||||||
tester ni construire le frontend.
|
tester ni construire le frontend.
|
||||||
|
|
||||||
Le frontend **n'a pas de cible dans le `Makefile` racine** et **aucun service dans
|
Le frontend a ses cibles dans le `Makefile` racine (`install-frontend`, `dev-frontend`,
|
||||||
`docker-compose.yml`** : il se pilote uniquement par `npm`, depuis `apps/frontend`. Le port 4200
|
englobées par `install` et `dev`), mais **aucun service dans `docker-compose.yml`** : en
|
||||||
|
développement il tourne toujours directement via `npm`, depuis `apps/frontend`. Le port 4200
|
||||||
n'apparaît dans le compose que comme valeur par défaut d'`APP_CORS_ORIGINS`, côté backend.
|
n'apparaît dans le compose que comme valeur par défaut d'`APP_CORS_ORIGINS`, côté backend.
|
||||||
|
|
||||||
Un `Dockerfile` frontend existe sur la branche `feat/pipeline-cd`, mais il est mono-étage et sans
|
Un `Dockerfile` frontend existe sur la branche `feat/pipeline-cd`, mais il est mono-étage et sans
|
||||||
|
|||||||
Reference in New Issue
Block a user