diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..4d97678 --- /dev/null +++ b/.env.example @@ -0,0 +1,15 @@ +# Variables lues par docker-compose.yml a la racine. +# Le backend lance hors conteneur (`make dev`) lit apps/backend/.env, pas ce fichier. + +POSTGRES_USER=enervision +POSTGRES_PASSWORD=change_me +POSTGRES_DB=enervision +# 5432 est souvent deja pris par une autre base du poste. +POSTGRES_PORT=5433 + +APP_ENV=local +APP_DEBUG=true +APP_LOG_LEVEL=INFO +APP_SECRET_KEY=change_me +APP_CORS_ORIGINS=http://localhost:4200 +BACKEND_PORT=8000 diff --git a/Makefile b/Makefile index 71dea97..aa29df8 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,8 @@ BACKEND := apps/backend .DEFAULT_GOAL := help -.PHONY: help install dev lint format typecheck test check docker-build +.PHONY: help install dev lint format typecheck test test-integration check docker-build \ + db-up db-down db-reset db-logs db-psql migrate 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}' @@ -21,10 +22,31 @@ format: ## Formate et corrige le backend typecheck: ## Verifie le typage du backend cd $(BACKEND) && uv run mypy app -test: ## Execute les tests backend +test: ## Execute les tests backend ne demandant pas de base cd $(BACKEND) && uv run pytest +test-integration: ## Execute les tests exigeant une base joignable + cd $(BACKEND) && uv run pytest -m integration + check: lint typecheck test ## Chaine de verification complete docker-build: ## Construit l'image du backend docker build -t enervision-backend:local $(BACKEND) + +db-up: ## Demarre la base PostgreSQL TimescaleDB + docker compose up -d db + +db-down: ## Arrete la base en conservant ses donnees + docker compose stop db + +db-reset: ## Detruit la base et rejoue db/init + docker compose down -v && docker compose up -d db + +db-logs: ## Suit les journaux de la base + docker compose logs -f db + +db-psql: ## Ouvre une session psql sur la base applicative + docker compose exec db psql -U $${POSTGRES_USER:-enervision} -d $${POSTGRES_DB:-enervision} + +migrate: ## Applique les migrations Alembic + cd $(BACKEND) && uv run alembic upgrade head diff --git a/db/README.md b/db/README.md index e80cb3c..c21a87d 100644 --- a/db/README.md +++ b/db/README.md @@ -1,6 +1,7 @@ # Base de donnees -PostgreSQL avec l'extension TimescaleDB. Non initialise, voir le ticket dedie. +PostgreSQL 17 avec l'extension TimescaleDB, servie en local par le service `db` du +`docker-compose.yml` racine (image `timescale/timescaledb-ha:pg17`). - `init` : scripts de bootstrap joues au premier demarrage du conteneur. - `migrations` : migrations SQL versionnees. @@ -8,3 +9,26 @@ PostgreSQL avec l'extension TimescaleDB. Non initialise, voir le ticket dedie. Les migrations du schema applicatif expose par l'API vivent dans `apps/backend/alembic`, pas ici. + +## `init` ne rejoue jamais + +Le dossier est monte sur `/docker-entrypoint-initdb.d`, dont PostgreSQL ne joue le +contenu qu'a la toute premiere initialisation, quand `PGDATA` est vide. Modifier ou +ajouter un script ensuite reste sans effet sur une base existante : + +```bash +docker compose down -v && docker compose up -d db +``` + +L'image joue d'abord ses propres scripts (`000_`, `001_`, `010_`), dont un +`CREATE EXTENSION IF NOT EXISTS timescaledb_toolkit CASCADE` qui installe `timescaledb` +au passage dans `postgres`, `template1` et la base applicative. Nos fichiers sont +numerotes a partir de `100` pour passer apres, quelle que soit la locale de tri. + +| Script | Role | +|---|---| +| `100-extensions.sql` | Declare explicitement les extensions attendues. | +| `110-test-database.sql` | Cree `enervision_test`, attendue par la suite de tests du backend. | + +Comme un bootstrap peut toujours avoir ete saute, c'est `/api/v1/health/ready` qui fait +foi : la sonde refuse de repondre 200 si l'extension n'est pas chargee. diff --git a/db/init/.gitkeep b/db/init/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/db/init/100-extensions.sql b/db/init/100-extensions.sql new file mode 100644 index 0000000..2326d5d --- /dev/null +++ b/db/init/100-extensions.sql @@ -0,0 +1,4 @@ +-- Piege : ce script ne rejoue qu'a la premiere initialisation, quand PGDATA est vide. +-- Le modifier ensuite reste sans effet tant que le volume n'est pas detruit. + +CREATE EXTENSION IF NOT EXISTS timescaledb; diff --git a/db/init/110-test-database.sql b/db/init/110-test-database.sql new file mode 100644 index 0000000..0f47b63 --- /dev/null +++ b/db/init/110-test-database.sql @@ -0,0 +1,8 @@ +-- Contrainte : le nom de cette base est code en dur dans apps/backend/tests/conftest.py. +-- Elle sert la suite de tests de la stack locale, pas un deploiement. + +CREATE DATABASE enervision_test; + +\connect enervision_test + +CREATE EXTENSION IF NOT EXISTS timescaledb; diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..e6854d8 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,43 @@ +# Piege : PGDATA de l'image timescaledb-ha vaut /home/postgres/pgdata/data, pas le chemin +# habituel de l'image postgres. Monte ailleurs, le volume ne retient rien, sans erreur. + +name: enervision + +services: + db: + image: timescale/timescaledb-ha:pg17 + environment: + POSTGRES_USER: ${POSTGRES_USER:?} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?} + POSTGRES_DB: ${POSTGRES_DB:?} + ports: + - "${POSTGRES_PORT:-5433}:5432" + volumes: + - pgdata:/home/postgres/pgdata/data + - ./db/init:/docker-entrypoint-initdb.d:ro + healthcheck: + test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"] + interval: 10s + timeout: 5s + retries: 12 + start_period: 40s + restart: unless-stopped + + backend: + build: ./apps/backend + depends_on: + db: + condition: service_healthy + environment: + APP_ENV: ${APP_ENV:-local} + APP_DEBUG: ${APP_DEBUG:-false} + APP_LOG_LEVEL: ${APP_LOG_LEVEL:-INFO} + APP_SECRET_KEY: ${APP_SECRET_KEY:?} + APP_CORS_ORIGINS: ${APP_CORS_ORIGINS:-http://localhost:4200} + DATABASE_URL: postgresql+asyncpg://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB} + ports: + - "${BACKEND_PORT:-8000}:8000" + restart: unless-stopped + +volumes: + pgdata: