Le DAG `alertes` enchaîne `app.detection.internal_alerts` puis `app.cli generate-recommendations`, à la quinzième minute de chaque heure. Le décalage laisse finir `ml_score`, qui écrit à l'heure pile les prédictions dont la règle `anomaly` a besoin, sans créer de dépendance entre les deux DAGs : quatre règles de détection sur cinq ne touchent pas au modèle, et un modèle jamais entraîné ne doit pas priver le parc de ses alertes. L'image Airflow porte un second environnement uv, `/opt/backend/.venv`, puisque la logique vit dans le backend (ADR 0006) et qu'aucune route HTTP ne l'expose. Le `UV_PROJECT_ENVIRONMENT` global hérité de l'issue #115 disparaît : il vaut pour tous les projets, donc `uv run` depuis `/opt/ml` résolvait le venv du backend. uv prend `<projet>/.venv` par défaut, se placer dans le dossier suffit. La CI vérifie maintenant que les deux environnements s'importent sans réseau. Le conteneur reçoit `DATABASE_URL` en asyncpg et une `APP_SECRET_KEY` distincte de celle de l'API, alimentée par `AIRFLOW_APP_SECRET_KEY` : la détection ne signe aucun jeton, et Airflow permet d'exécuter du code depuis son interface.
46 lines
2.0 KiB
Bash
46 lines
2.0 KiB
Bash
# Variables lues par docker-compose.yml à la racine.
|
|
# Le backend lancé hors conteneur (`make dev`) lit apps/backend/.env, pas ce fichier.
|
|
|
|
POSTGRES_USER=enervision
|
|
POSTGRES_PASSWORD=change_me
|
|
POSTGRES_DB=enervision
|
|
# 5432 est souvent déjà pris par une autre base du poste.
|
|
POSTGRES_PORT=5433
|
|
# `basic` renvoie des statistiques d'usage à Timescale.
|
|
TIMESCALEDB_TELEMETRY=off
|
|
|
|
APP_ENV=local
|
|
APP_DEBUG=false
|
|
APP_LOG_LEVEL=INFO
|
|
# L'API refuse de démarrer tant que cette valeur reste un exemple ou fait moins de
|
|
# 32 caractères. Générer la vôtre : python -c "import secrets; print(secrets.token_urlsafe(48))"
|
|
APP_SECRET_KEY=change_me
|
|
APP_CORS_ORIGINS=http://localhost:4200
|
|
BACKEND_PORT=8000
|
|
|
|
# API Mock EnerVision
|
|
APP_MOCK_API_BASE_URL=https://api-mock.charlieandre.fr
|
|
APP_MOCK_API_USERNAME=change_me
|
|
APP_MOCK_API_PASSWORD=change_me
|
|
APP_MOCK_API_TIMEOUT_SECONDS=10
|
|
|
|
# Airflow (webserver + scheduler, LocalExecutor). Base de métadonnées dédiée `airflow` dans le
|
|
# même conteneur `db` (cf. db/init/120-airflow-database.sql), pas un conteneur de plus.
|
|
AIRFLOW_PORT=8080
|
|
# Chiffre les connexions/variables stockées par Airflow. Générer la vôtre :
|
|
# python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
|
|
AIRFLOW_FERNET_KEY=change_me
|
|
# Clé Flask du webserver Airflow (signature de session), distincte de la précédente. Générer la
|
|
# vôtre : python -c "import secrets; print(secrets.token_urlsafe(48))"
|
|
AIRFLOW_WEBSERVER_SECRET_KEY=change_me
|
|
AIRFLOW_ADMIN_USERNAME=admin
|
|
# Compte Airflow créé au premier démarrage (service `airflow-init`), sans rapport avec les
|
|
# comptes `app_user` d'EnerVision.
|
|
AIRFLOW_ADMIN_PASSWORD=change_me
|
|
AIRFLOW_ADMIN_EMAIL=admin@enervision.fr
|
|
# `APP_SECRET_KEY` du backend, que le DAG `alertes` lance en sous-processus. Distincte de
|
|
# celle de l'API : la détection ne signe aucun jeton, et Airflow exécute du code depuis son
|
|
# interface (cf. ADR 0008). Générer la vôtre :
|
|
# python -c "import secrets; print(secrets.token_urlsafe(48))"
|
|
AIRFLOW_APP_SECRET_KEY=change_me
|