|
|
|
@@ -44,7 +44,7 @@
|
|
|
|
|
- **Service externe — adresses** : API BAN (Base Adresses Nationale) — `https://api-adresse.data.gouv.fr` — autocomplétion et géocodage des adresses françaises, sans clé d'authentification
|
|
|
|
|
- **Service externe — itinéraires** : OpenRouteService (ORS) — calcul de distance routière et géométrie d'itinéraire entre coordonnées géographiques connues
|
|
|
|
|
- **Génération PDF** : côté backend, bibliothèque QuestPDF
|
|
|
|
|
- **Hébergement** : l'application est packagée et hébergée via **Docker Compose** — un conteneur `frontend` (Angular servi par nginx en interne) publié sur le port hôte **2202**, et un conteneur `backend` (ASP.NET Core API) non exposé à l'hôte. Le fichier SQLite est persisté sur un volume monté dans le conteneur backend. La terminaison **HTTPS est assurée par un reverse-proxy Caddy externe** (sur une autre machine) qui relaie vers `IP_hôte:2202` ; le backend ne fait donc pas de redirection HTTPS lui-même
|
|
|
|
|
- **Hébergement** : l'application est packagée et hébergée via **Docker Compose** — un conteneur `frontend` (Angular servi par nginx en interne) publié sur le port hôte **2202**, et un conteneur `backend` (ASP.NET Core API) non exposé à l'hôte. Le fichier SQLite est persisté via un **bind mount** (`./data:/data`) dans le conteneur backend, ce qui rend `data/trajets.db` directement accessible sur l'hôte (inspection et sauvegarde sans entrer dans le conteneur). La terminaison **HTTPS est assurée par un reverse-proxy Caddy externe** (sur une autre machine) qui relaie vers `IP_hôte:2202` ; le backend ne fait donc pas de redirection HTTPS lui-même
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
@@ -121,7 +121,7 @@ sequenceDiagram
|
|
|
|
|
|
|
|
|
|
> **Justification** : L'application est mono-utilisateur et n'a pas vocation à être multi-instances. SQLite élimine la complexité d'installation d'un serveur de base de données, offre des transactions ACID complètes via Entity Framework Core, et est parfaitement supporté par EF Core avec les migrations standard. Le fichier `trajets.db` est autoportant et facilement sauvegardable.
|
|
|
|
|
>
|
|
|
|
|
> **Emplacement en conteneur** : dans le déploiement Docker Compose, le fichier de base de données est stocké sous le chemin du volume monté `/data/trajets.db` (voir la section « Déploiement (Docker Compose) »). Le chemin est fourni au backend via la chaîne de connexion / variable d'environnement (`ConnectionStrings__Default=Data Source=/data/trajets.db`), ce qui garantit la persistance des données indépendamment du cycle de vie du conteneur.
|
|
|
|
|
> **Emplacement en conteneur** : dans le déploiement Docker Compose, le fichier de base de données est stocké sous `/data/trajets.db`, monté via un **bind mount** depuis `./data` de l'hôte (voir la section « Déploiement (Docker Compose) » et « Accès à la base de données »). Le chemin est fourni au backend via la chaîne de connexion / variable d'environnement (`ConnectionStrings__Default=Data Source=/data/trajets.db`), ce qui garantit la persistance des données indépendamment du cycle de vie du conteneur.
|
|
|
|
|
|
|
|
|
|
### Décision d'architecture : BAN pour le géocodage, ORS pour l'itinéraire
|
|
|
|
|
|
|
|
|
@@ -1554,7 +1554,7 @@ La génération PDF est enveloppée dans un `CancellationToken` avec délai de 1
|
|
|
|
|
|
|
|
|
|
La solution est hébergée via **Docker Compose** avec deux services :
|
|
|
|
|
|
|
|
|
|
- **`backend`** : API ASP.NET Core (.NET 8), exposant le port `8080` en interne. Le fichier SQLite est stocké sous le chemin du volume monté `/data/trajets.db`.
|
|
|
|
|
- **`backend`** : API ASP.NET Core (.NET 8), exposant le port `8080` en interne. Le fichier SQLite est stocké sous `/data/trajets.db`, monté depuis `./data` de l'hôte via un bind mount (accès direct au fichier).
|
|
|
|
|
- **`frontend`** : application Angular compilée et servie par **nginx**. Nginx sert la SPA (fallback vers `index.html` pour le routage côté client) et agit comme reverse-proxy en relayant les requêtes `/api` vers le service `backend`. C'est le seul service exposé à l'hôte (port **`2202`** de l'hôte → nginx). La terminaison HTTPS est assurée par un reverse-proxy **Caddy externe**.
|
|
|
|
|
|
|
|
|
|
```mermaid
|
|
|
|
@@ -1564,7 +1564,7 @@ graph LR
|
|
|
|
|
NGINX[frontend<br/>nginx + SPA Angular]
|
|
|
|
|
API[backend<br/>ASP.NET Core :8080]
|
|
|
|
|
NGINX -- "proxy /api → backend:8080" --> API
|
|
|
|
|
API -- "/data/trajets.db" --> VOL[(Volume nommé<br/>trajets-data)]
|
|
|
|
|
API -- "/data/trajets.db" --> VOL[(Bind mount<br/>./data sur l'hôte)]
|
|
|
|
|
end
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
@@ -1664,7 +1664,8 @@ services:
|
|
|
|
|
# URL publique du frontend (liens de réinitialisation de mot de passe), ex. https://mon-domaine
|
|
|
|
|
- Auth__FrontendBaseUrl=${AUTH_FRONTEND_BASE_URL}
|
|
|
|
|
volumes:
|
|
|
|
|
- trajets-data:/data
|
|
|
|
|
# Bind mount : base accessible sur l'hôte dans ./data/trajets.db
|
|
|
|
|
- ./data:/data
|
|
|
|
|
restart: unless-stopped
|
|
|
|
|
healthcheck:
|
|
|
|
|
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
|
|
|
|
@@ -1683,9 +1684,6 @@ services:
|
|
|
|
|
backend:
|
|
|
|
|
condition: service_healthy
|
|
|
|
|
restart: unless-stopped
|
|
|
|
|
|
|
|
|
|
volumes:
|
|
|
|
|
trajets-data: # persistance SQLite au-delà du cycle de vie des conteneurs
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
> **Endpoint de santé** : le backend expose `GET /health` (via `AddHealthChecks()` / `MapHealthChecks("/health")`) utilisé par le `healthcheck` Compose et la condition `depends_on: service_healthy`.
|
|
|
|
@@ -1697,7 +1695,7 @@ volumes:
|
|
|
|
|
- L'**URL publique du frontend** (`AUTH_FRONTEND_BASE_URL`, ex. `https://mon-domaine`) sert à construire les liens de réinitialisation de mot de passe envoyés par e-mail — et non le port interne.
|
|
|
|
|
- La **configuration e-mail** (SMTP via MailKit) est optionnelle : si elle est absente, le lien de réinitialisation est journalisé en repli (fallback) au lieu d'être envoyé.
|
|
|
|
|
- L'**API BAN** ne nécessite aucune clé — aucune configuration de secret n'est requise pour le géocodage.
|
|
|
|
|
- Le **volume nommé `trajets-data`** (monté sur `/data`) garantit que le fichier `trajets.db` persiste à travers les redémarrages, recréations et mises à jour des conteneurs.
|
|
|
|
|
- Le **bind mount `./data:/data`** garantit que le fichier `trajets.db` persiste à travers les redémarrages, recréations et mises à jour des conteneurs, tout en le rendant directement accessible sur l'hôte.
|
|
|
|
|
|
|
|
|
|
### Migrations au démarrage
|
|
|
|
|
|
|
|
|
@@ -1705,6 +1703,41 @@ Les **migrations EF Core sont appliquées automatiquement au démarrage du backe
|
|
|
|
|
|
|
|
|
|
> **Documents de spécification embarqués** : le `Dockerfile` backend a pour contexte la racine du dépôt et copie `.kiro/specs/schotziad-km-tracker/{requirements,design,tasks}.md` dans `./specs/` de l'image. Le `SpecDocumentService` lit en priorité ce dossier embarqué (`ContentRootPath/specs`), avec repli sur `.kiro/specs/...` en développement. L'application est ainsi autonome, sans montage du dépôt à l'exécution.
|
|
|
|
|
|
|
|
|
|
### Accès à la base de données
|
|
|
|
|
|
|
|
|
|
La base SQLite est exposée à l'hôte via un **bind mount** (`./data:/data` dans `docker-compose.yml`) : le fichier `data/trajets.db` est directement lisible et sauvegardable **sans entrer dans le conteneur**, à côté du `docker-compose.yml`.
|
|
|
|
|
|
|
|
|
|
**Inspection / lecture** : ouvrir `data/trajets.db` avec un outil SQLite (DB Browser for SQLite, `sqlite3`, extension VS Code…). La lecture pendant que l'application tourne est sûre.
|
|
|
|
|
|
|
|
|
|
**Sauvegarde à froid (recommandée)** : SQLite tolère mal l'écriture concurrente. Pour un backup cohérent, arrêter le backend le temps de la copie, ou utiliser la commande de sauvegarde en ligne intégrée :
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# Option 1 — snapshot à froid (cohérence garantie)
|
|
|
|
|
docker compose stop backend
|
|
|
|
|
cp data/trajets.db "backup-$(date +%F).db"
|
|
|
|
|
docker compose start backend
|
|
|
|
|
|
|
|
|
|
# Option 2 — sauvegarde en ligne SQLite (sans arrêt), si sqlite3 est installé sur l'hôte
|
|
|
|
|
sqlite3 data/trajets.db ".backup 'backup-$(date +%F).db'"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
> **Concurrence** : éviter d'écrire dans `data/trajets.db` depuis l'extérieur pendant que l'application tourne (risque de corruption). La lecture reste sûre.
|
|
|
|
|
|
|
|
|
|
**Migration depuis l'ancien volume nommé** : les déploiements antérieurs utilisaient un volume nommé Docker (`trajets-data`). Pour récupérer ces données dans le nouveau bind mount `./data` avant de basculer :
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# Extraire la base de l'ancien volume vers ./data (backend encore basé sur l'ancienne image)
|
|
|
|
|
mkdir -p data
|
|
|
|
|
docker compose cp backend:/data/trajets.db ./data/trajets.db # si le conteneur tourne encore
|
|
|
|
|
# — ou, si le service est déjà recréé, copier depuis le volume nommé :
|
|
|
|
|
docker run --rm -v trajets-data:/from -v "$PWD/data":/to alpine \
|
|
|
|
|
sh -c "cp -a /from/. /to/"
|
|
|
|
|
# Puis recréer avec le bind mount :
|
|
|
|
|
docker compose up -d --build
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Après migration et vérification, l'ancien volume nommé peut être supprimé : `docker volume rm trajets-data`.
|
|
|
|
|
|
|
|
|
|
> **Migrations notables** : `VehiculeDenomination` (dénomination du véhicule), `TrajetGeometrieJson` (géométrie d'itinéraire portée par le trajet), `TrancheBaremeKmMaxNullable` / bornes exclusives / coefficient numérique (barème multi-tranches à dernière tranche illimitée), `AjoutAuthentification` (comptes + jetons de réinitialisation), `TrajetAllerRetour` (indicateur aller/retour).
|
|
|
|
|
|
|
|
|
|
## Error Handling
|
|
|
|
|