Files
SchotzIAD/run-dev.sh
T
MadJax 0e5b1c4d0d Dev : proxy /api paramétrable et arrêt propre du groupe de process
- run-dev.sh s'exécute dans sa propre session (setsid) et tue le GROUPE
  de process entier au cleanup : le backend (Kestrel) et ses enfants
  sont bien arrêtés, le port backend est réellement libéré à l'arrêt
- Port backend paramétrable via BACKEND_PORT (défaut 8080), transmis à
  la fois au backend (dotnet --urls) et au proxy de dev du front
- Nouveau frontend/proxy.conf.js : ng serve relaie /api vers
  http://localhost:${BACKEND_PORT} ; référencé dans angular.json (serve)
- environment.ts (dev) : apiUrl passe à '/api' (same-origin via proxy),
  le port du backend n'est plus codé en dur dans le front
- Prod inchangée (environment.prod.ts déjà en '/api', nginx)
2026-10-13 08:30:25 +02:00

170 lines
7.8 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# Lance en mode développement le backend (port BACKEND_PORT, défaut 8080) et le
# frontend Angular (ng serve, port 4200) avec la base SQLite réelle.
# À utiliser hors Docker, pour le développement local.
#
# Usage : ./run-dev.sh
# Arrêt : Ctrl+C (backend ET frontend sont arrêtés ensemble, port backend libéré).
#
# Le port du backend est paramétrable via BACKEND_PORT (ex. BACKEND_PORT=5080 ./run-dev.sh).
# Ce même port est transmis au backend (dotnet --urls) ET au proxy de dev du front
# (proxy.conf.js lit BACKEND_PORT), de sorte qu'il n'est défini qu'à un seul endroit.
set -uo pipefail
# --- Groupe de process dédié ------------------------------------------------
# Problème résolu : à l'arrêt, « dotnet run » / « npm start » lancent des process
# ENFANTS (le vrai serveur Kestrel, esbuild…) qui survivaient à un simple kill du
# PID parent, laissant le port backend occupé. Solution : exécuter tout le script
# dans sa PROPRE session/groupe de process (via setsid), puis tuer le GROUPE ENTIER
# au cleanup. On se relance une seule fois sous setsid si ce n'est pas déjà le cas.
if [[ -z "${RUN_DEV_SETSID:-}" ]] && command -v setsid >/dev/null 2>&1; then
export RUN_DEV_SETSID=1
exec setsid "$0" "$@"
fi
RACINE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Port du backend (paramétrable). Transmis au backend et au proxy de dev du front.
export BACKEND_PORT="${BACKEND_PORT:-8080}"
# --- Backend (.NET 8) -------------------------------------------------------
export PATH="$HOME/.dotnet:$HOME/.dotnet/tools:$PATH"
export DOTNET_ROOT="$HOME/.dotnet"
# Base réelle, chemin ABSOLU pour éviter deux bases selon le dossier de lancement.
export ConnectionStrings__Default="Data Source=$RACINE/backend/SchotzIadKmTracker.Api/trajets.db"
export ASPNETCORE_ENVIRONMENT=Development
# Clé OpenRouteService : lue depuis .env (ORS_API_KEY) et injectée sous le nom attendu
# par la configuration .NET (ORS__ApiKey). Sans clé valide, le backend calcule les
# distances à vol d'oiseau (fallback Haversine) au lieu de la distance routière réelle.
if [[ -z "${ORS__ApiKey:-}" && -f "$RACINE/.env" ]]; then
ORS_KEY_FROM_ENV="$(grep -E '^ORS_API_KEY=' "$RACINE/.env" | head -n1 | cut -d= -f2- || true)"
if [[ -n "$ORS_KEY_FROM_ENV" ]]; then
export ORS__ApiKey="$ORS_KEY_FROM_ENV"
echo "→ Clé ORS chargée depuis .env (calcul de distance routière activé)."
else
echo "⚠ Aucune clé ORS dans .env : distances calculées à vol d'oiseau."
fi
fi
# --- Authentification (dev) -------------------------------------------------
# Clé de signature JWT : lue depuis .env (JWT_SECRET_KEY). Si absente, on génère
# un secret ALÉATOIRE de développement (valable pour cette session). En dev, cela
# signifie que les sessions existantes sont invalidées à chaque relance du script,
# ce qui est acceptable ; en production, définis JWT_SECRET_KEY dans .env.
if [[ -z "${JWT__SecretKey:-}" ]]; then
JWT_KEY_FROM_ENV=""
if [[ -f "$RACINE/.env" ]]; then
JWT_KEY_FROM_ENV="$(grep -E '^JWT_SECRET_KEY=' "$RACINE/.env" | head -n1 | cut -d= -f2- || true)"
fi
# Ignore la valeur placeholder du .env.example si elle a été copiée telle quelle.
if [[ -n "$JWT_KEY_FROM_ENV" && "$JWT_KEY_FROM_ENV" != "change-me-generate-a-long-random-secret" ]]; then
export JWT__SecretKey="$JWT_KEY_FROM_ENV"
echo "→ Clé JWT chargée depuis .env."
else
# Secret de dev aléatoire (>= 32 octets) : openssl si dispo, sinon /dev/urandom.
if command -v openssl >/dev/null 2>&1; then
export JWT__SecretKey="$(openssl rand -base64 48)"
else
export JWT__SecretKey="$(head -c 48 /dev/urandom | base64)"
fi
echo "⚠ Aucune clé JWT dans .env : secret de développement aléatoire généré (sessions invalidées à chaque relance)."
fi
fi
# URL de base du frontend pour les liens de réinitialisation (dev = ng serve sur 4200).
export Auth__FrontendBaseUrl="${Auth__FrontendBaseUrl:-http://localhost:4200}"
# SMTP non configuré en dev : le lien de réinitialisation est journalisé dans la
# sortie du backend (repli LoggingEmailService). Cherche « reset-password?token= »
# dans les logs pour tester la réinitialisation sans email réel.
echo "→ Démarrage du backend sur http://localhost:$BACKEND_PORT (base : $RACINE/backend/SchotzIadKmTracker.Api/trajets.db)"
dotnet run --project "$RACINE/backend/SchotzIadKmTracker.Api" --urls "http://localhost:$BACKEND_PORT" &
PID_BACK=$!
# --- Frontend (Angular via nvm/Node 22) -------------------------------------
export NVM_DIR="$HOME/.nvm"
# shellcheck source=/dev/null
. "$NVM_DIR/nvm.sh"
nvm use 22 >/dev/null
# Vide le cache de compilation Angular pour garantir un bundle à jour, notamment
# le polyfill zone.js (sans lui, la vue ne se rafraîchit pas sur les réponses HTTP :
# `window.Zone` doit être défini au runtime). Un cache périmé peut resservir un
# ancien polyfills sans zone.js.
echo "→ Nettoyage du cache Angular (.angular/cache)"
rm -rf "$RACINE/frontend/.angular/cache"
echo "→ Démarrage du frontend sur http://localhost:4200 (proxy /api → :$BACKEND_PORT ; première compilation : patiente…)"
( cd "$RACINE/frontend" && npm start ) &
PID_FRONT=$!
# --- Arrêt propre : on tue TOUT le groupe de process ------------------------
# Comme le script tourne dans sa propre session (setsid), son PID est aussi le PGID.
# `kill -- -$$` envoie le signal à TOUT le groupe (backend, frontend et leurs enfants),
# ce qui libère réellement le port backend. Repli ciblé par sécurité.
cleanup() {
echo ""
echo "→ Arrêt (groupe de process)…"
# Signal au groupe entier (le '-' devant le PID = groupe). $$ = PID du script = PGID.
kill -TERM -- "-$$" 2>/dev/null || true
# Repli : au cas où certains process ne seraient pas dans le groupe.
kill "$PID_BACK" "$PID_FRONT" 2>/dev/null || true
pkill -f "SchotzIadKmTracker.Api" 2>/dev/null || true
}
trap cleanup EXIT INT TERM
# --- Attente active : ng serve prêt à répondre en HTTP 200 ------------------
# La première compilation (surtout après vidage du cache) peut prendre 30–60 s.
# Tant que ng serve n'a pas fini, le port répond « connexion refusée » (HTTP 000)
# et le navigateur chargerait une page incomplète (zone.js absent → window.Zone
# undefined). On ne montre l'invitation qu'une fois le serveur réellement prêt.
attendre_frontend() {
local url="http://localhost:4200/"
local max=120 # secondes
local t=0
printf "→ Compilation du frontend"
while [ "$t" -lt "$max" ]; do
# Le process ng serve est-il toujours vivant ?
if ! kill -0 "$PID_FRONT" 2>/dev/null; then
echo ""
echo "✗ Le frontend s'est arrêté avant d'être prêt (voir les logs ci-dessus)."
return 1
fi
local code
code=$(curl -s -o /dev/null -w "%{http_code}" "$url" 2>/dev/null || echo 000)
if [ "$code" = "200" ]; then
echo " prêt."
return 0
fi
printf "."
sleep 2
t=$((t + 2))
done
echo ""
echo "✗ Le frontend n'a pas répondu 200 au bout de ${max}s. Vérifie les logs ng serve."
return 1
}
if attendre_frontend; then
echo ""
echo "======================================================================"
echo " Application prête."
echo " Backend : http://localhost:$BACKEND_PORT/api"
echo " Frontend : http://localhost:4200 ← ouvre cette URL maintenant"
echo ""
echo " Au premier chargement, fais Ctrl+Shift+R pour vider le cache navigateur."
echo " Vérif : dans la console DevTools, 'window.Zone' doit renvoyer un objet."
echo ""
echo " Auth : à la 1re utilisation, crée le compte via /register puis connecte-toi."
echo " Reset de mot de passe (dev) : le lien est journalisé dans la sortie backend"
echo " ci-dessus (cherche « reset-password?token= »), aucun email n'est envoyé."
echo "======================================================================"
echo ""
fi
wait