#!/bin/bash # Stack webapp LiDAR — gestion de la stack compose sur la machine légère # (Raspberry Pi). Cf. docs/DEPLOY_WEBAPP.md. # # Pilote docker compose (jamais docker run direct, qui ignorerait labels et # volumes de l'override) : # - docker-compose.webapp.yml (image légère Dockerfile.webapp, port 8973) ; # - docker-compose.webapp.override.yml s'il existe (non versionné : labels # Traefik, volume réel, environnement local) — chargé automatiquement : # la route publique HTTPS et le cache de tuiles réels sont préservés. # # Configuration : variables dans webapp.env (modèle : webapp.env.example, # non versionné) ou exportées — elles alimentent l'interpolation compose # (${LIDAR_WEBAPP_HOST} des labels Traefik, etc.) ; l'environnement du # CONTENEUR vit dans les fichiers compose / override. # # HTTPS : terminé par Traefik (override). Sans Traefik, monter ./tls via un # override local avec LIDAR_SSL_CERTFILE / LIDAR_SSL_KEYFILE (cf. # make-tls-cert.sh) — ce script ne gère plus le certificat auto-signé. # # Usage : # ./serve-webapp.sh [start] démarrer (build + arrière-plan, redémarrage auto) # ./serve-webapp.sh stop arrêter et supprimer la stack (cache conservé) # ./serve-webapp.sh restart reconstruire l'image et recréer le conteneur # ./serve-webapp.sh redeploir git pull + rebuild de l'image + redémarrage du conteneur # ./serve-webapp.sh status état du conteneur et de la dernière sync # ./serve-webapp.sh sync forcer un rebuild + régénération des vignettes # ./serve-webapp.sh logs suivre les logs du conteneur set -e SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" CONTAINER="lidar-webapp" COMPOSE_BASE="docker-compose.webapp.yml" COMPOSE_OVERRIDE="docker-compose.webapp.override.yml" # Configuration locale (non versionnée) : webapp.env surcharge l'environnement # (sert à l'interpolation ${...} des fichiers compose). if [ -f "${SCRIPT_DIR}/webapp.env" ]; then set -a # shellcheck disable=SC1091 . "${SCRIPT_DIR}/webapp.env" set +a fi WEBAPP_PORT="${WEBAPP_PORT:-8973}" COMPOSE_FILES=(-f "${SCRIPT_DIR}/${COMPOSE_BASE}") if [ -f "${SCRIPT_DIR}/${COMPOSE_OVERRIDE}" ]; then COMPOSE_FILES+=(-f "${SCRIPT_DIR}/${COMPOSE_OVERRIDE}") fi compose() { docker compose "${COMPOSE_FILES[@]}" "$@" } # Port hôte réellement publié par le conteneur (mapping compose) ; repli sur # WEBAPP_PORT tant que le conteneur est arrêté. published_port() { local p p="$(docker port "$CONTAINER" 8973/tcp 2>/dev/null | head -1 | sed 's/.*://')" echo "${p:-$WEBAPP_PORT}" } lan_ip() { hostname -I 2> /dev/null | awk '{print $1}' } wait_up() { echo -n "Attente du serveur sur le port $1..." local up=0 i for i in $(seq 1 30); do if curl -fsS "http://127.0.0.1:$1/api/status" > /dev/null 2>&1; then up=1 break fi sleep 1 done if [ "$up" != "1" ]; then echo " KO (voir ./serve-webapp.sh logs)" >&2 exit 1 fi echo " OK" } cmd_start() { compose up -d --build wait_up "$(published_port)" local port port="$(published_port)" echo "============================================" echo " Carte LiDAR — webapp (${CONTAINER})" echo "============================================" echo " URL : http://127.0.0.1:${port}/" if [ -n "$(lan_ip)" ]; then echo " URL (LAN) : http://$(lan_ip):${port}/" fi if [ -f "${SCRIPT_DIR}/${COMPOSE_OVERRIDE}" ]; then echo " Override compose : ${COMPOSE_OVERRIDE} chargé (Traefik/volume local)" else echo " Override compose : absent (HTTP direct, sans Traefik)" fi echo " Génération distante: ${LIDAR_GENERATION_URL:-non configurée (override / webapp.env)}" echo "============================================" } cmd_stop() { compose down echo "Stack webapp arrêtée (conteneur ${CONTAINER} supprimé, cache conservé)." } cmd_restart() { compose up -d --build --force-recreate wait_up "$(published_port)" echo "Stack webapp recréée (image reconstruite, relit webapp.env)." } # Mise à jour complète du checkout git + rebuild de l'image + redémarrage du # conteneur. À lancer sur le Pi après un push vers origin : récupère les # derniers commits, reconstruit l'image légère et recrée le conteneur. Le # cache de tuiles (output/) est conservé — les vignettes déjà présentes ne # sont pas régénérées (voir sync pour forcer un rebuild des vignettes). cmd_redeploy() { if [ ! -d "${SCRIPT_DIR}/.git" ]; then echo "Échec : ${SCRIPT_DIR} n'est pas un dépôt git — impossible de faire git pull." >&2 exit 1 fi echo "============================================" echo " Redéploiement webapp (${CONTAINER})" echo "============================================" echo " Checkout git : ${SCRIPT_DIR}" echo " Récupération des derniers commits (git pull)..." git -C "${SCRIPT_DIR}" pull --ff-only echo " Image reconstruite, conteneur recréé..." compose up -d --build --force-recreate wait_up "$(published_port)" local port port="$(published_port)" echo "============================================" echo " Webapp redéployée — http://127.0.0.1:${port}/" echo "============================================" } cmd_status() { compose ps local port port="$(published_port)" if curl -fsS "http://127.0.0.1:${port}/api/sync" 2> /dev/null; then echo "" else echo "Webapp : injoignable sur http://127.0.0.1:${port}" >&2 fi } cmd_sync() { local port code port="$(published_port)" code="$(curl -s -o /dev/null -w '%{http_code}' -X POST "http://127.0.0.1:${port}/api/sync" || true)" case "$code" in 200) echo "Sync lancée (suivi : ./serve-webapp.sh status, logs : ./serve-webapp.sh logs)" ;; 409) echo "Une sync est déjà en cours (suivi : ./serve-webapp.sh status)" ;; *) echo "Échec : HTTP ${code:-pas de réponse} (webapp injoignable sur http://127.0.0.1:${port} ?)" >&2 exit 1 ;; esac } CMD="${1:-start}" case "$CMD" in start) cmd_start ;; stop) cmd_stop ;; restart) cmd_restart ;; redeploir) cmd_redeploy ;; status) cmd_status ;; sync) cmd_sync ;; logs) compose logs -f webapp ;; *) echo "Usage : $0 [start|stop|restart|redeploir|status|sync|logs]" >&2 exit 1 ;; esac