#!/bin/bash # Stack webapp LiDAR — gestion du conteneur carte interactive sur la machine # légère (Raspberry Pi). Cf. docs/DEPLOY_WEBAPP.md. # # Configuration : variables dans webapp.env (modèle : webapp.env.example, # non versionné) ou exportées dans l'environnement : # WEBAPP_PORT port hôte (défaut 8973) # LIDAR_GENERATION_URL machine de traitement (ex. http://192.168.1.50:8973) # LIDAR_REMOTE_TOKEN jeton si la machine exige LIDAR_API_TOKEN # LIDAR_REGEN_CIDR réseau autorisé à lancer les générations # (défaut 192.168.1.0/24, vide = restriction levée) # # GPS sur téléphone (optionnel) : l'API Geolocation exige une connexion # sécurisée. ./make-tls-cert.sh crée un certificat auto-signé dans ./tls ; # dès qu'il est présent, ce script sert la carte en HTTPS (https://:port) # et le bouton ⌖ « centrer sur la position GPS » fonctionne sur téléphone # (accepter l'avertissement « certificat non fiable » une fois). # # Usage : # ./serve-webapp.sh [start] démarrer (arrière-plan, redémarrage auto) # ./serve-webapp.sh stop arrêter et supprimer le conteneur # ./serve-webapp.sh restart redémarrer (relit webapp.env) # ./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)" IMAGE="lidar-webapp" CONTAINER="lidar-webapp" # Configuration locale (non versionnée) : webapp.env surcharge l'environnement if [ -f "${SCRIPT_DIR}/webapp.env" ]; then set -a # shellcheck disable=SC1091 . "${SCRIPT_DIR}/webapp.env" set +a fi WEBAPP_PORT="${WEBAPP_PORT:-8973}" # HTTPS (requis pour la géolocalisation GPS depuis un téléphone) : l'API # Geolocation n'existe qu'en contexte sécurisé. Si le certificat auto-signé # ./tls (cf. ./make-tls-cert.sh) est présent, la carte est servie en # https://:PORT ; le téléphone accepte l'avertissement « certificat non # fiable » une fois. Sans certificat : HTTP (le bouton GPS est indisponible). TLS_DIR="${SCRIPT_DIR}/tls" if [ -f "${TLS_DIR}/webapp.crt" ] && [ -f "${TLS_DIR}/webapp.key" ]; then SCHEME=https KFLAG=" -k" # -k : les sondes locales tolèrent le certificat auto-signé else SCHEME=http KFLAG="" fi BASE_URL="${SCHEME}://127.0.0.1:${WEBAPP_PORT}" CMD="${1:-start}" build_image() { echo "Build de l'image webapp (cache Docker)..." if ! docker build -f "${SCRIPT_DIR}/Dockerfile.webapp" -t "$IMAGE" "$SCRIPT_DIR" > /tmp/lidar_webapp_build.log 2>&1; then cat /tmp/lidar_webapp_build.log >&2 echo "Échec du build (journal : /tmp/lidar_webapp_build.log)" >&2 exit 1 fi } lan_ip() { hostname -I 2> /dev/null | awk '{print $1}' } cmd_start() { mkdir -p "${SCRIPT_DIR}/output" if curl -fsS${KFLAG} "${BASE_URL}/api/status" > /dev/null 2>&1; then echo "Le port ${WEBAPP_PORT} répond déjà (webapp déjà lancée ?)." >&2 echo "Arrêter l'existant, ou relancer avec un autre port : WEBAPP_PORT=9000 $0" >&2 exit 1 fi if ! docker image inspect "$IMAGE" > /dev/null 2>&1; then build_image fi ENV_ARGS=() if [ -n "${LIDAR_GENERATION_URL:-}" ]; then ENV_ARGS+=(-e LIDAR_GENERATION_URL="$LIDAR_GENERATION_URL") fi if [ -n "${LIDAR_REMOTE_TOKEN:-}" ]; then ENV_ARGS+=(-e LIDAR_REMOTE_TOKEN="$LIDAR_REMOTE_TOKEN") fi # Définie même vide (= restriction levée) ; absente = défaut webapp.py if [ -n "${LIDAR_REGEN_CIDR+x}" ]; then ENV_ARGS+=(-e LIDAR_REGEN_CIDR="${LIDAR_REGEN_CIDR:-}") fi # Certificat auto-signé (HTTPS → GPS téléphone) : monté en lecture seule # et activé via LIDAR_SSL_CERTFILE / LIDAR_SSL_KEYFILE (cf. make-tls-cert.sh). TLS_ARGS=() if [ "$SCHEME" = "https" ]; then TLS_ARGS+=(-v "${TLS_DIR}:/app/tls:ro") TLS_ARGS+=(-e LIDAR_SSL_CERTFILE=/app/tls/webapp.crt) TLS_ARGS+=(-e LIDAR_SSL_KEYFILE=/app/tls/webapp.key) fi # Clé SSH du hôte (déploiement, git pull distant) : montée en lecture seule si présente SSH_ARGS=() if [ -d "${HOME}/.ssh" ]; then SSH_ARGS+=(-v "${HOME}/.ssh:/home/lidar/.ssh:ro") fi docker rm -f "$CONTAINER" > /dev/null 2>&1 || true docker run -d --init \ --name "$CONTAINER" \ --restart unless-stopped \ --user 1000:1000 \ -p "${WEBAPP_PORT}:8973" \ -v "${SCRIPT_DIR}/output:/data/output" \ "${TLS_ARGS[@]}" \ "${SSH_ARGS[@]}" \ -e LIDAR_OUTPUT_DIR=/data/output \ "${ENV_ARGS[@]}" \ "$IMAGE" > /dev/null echo -n "Attente du serveur sur le port ${WEBAPP_PORT}..." UP=0 for _ in $(seq 1 30); do if curl -fsS${KFLAG} "${BASE_URL}/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" echo "============================================" echo " Carte LiDAR — webapp (${CONTAINER})" echo "============================================" echo " URL : ${BASE_URL}/" if [ -n "$(lan_ip)" ]; then echo " URL (LAN) : ${SCHEME}://$(lan_ip):${WEBAPP_PORT}/" fi if [ "$SCHEME" = "https" ]; then echo " HTTPS (GPS tél.) : actif — sur le téléphone, acceptez l'avertissement « certificat non fiable »" fi echo " Génération distante: ${LIDAR_GENERATION_URL:-non configurée}" echo " Cache local tuiles : à la demande (LIDAR_GENERATION_URL)" echo "============================================" } cmd_stop() { docker rm -f "$CONTAINER" > /dev/null 2>&1 || true echo "Conteneur ${CONTAINER} arrêté." } cmd_status() { if docker ps --format '{{.Names}}' 2> /dev/null | grep -qx "$CONTAINER"; then echo "Conteneur : ${CONTAINER} en cours d'exécution ($(docker ps --filter name="^${CONTAINER}$" --format '{{.Status}}'))" else echo "Conteneur : ${CONTAINER} arrêté" fi if curl -fsS${KFLAG} "${BASE_URL}/api/sync" 2> /dev/null; then echo "" else echo "Webapp : injoignable sur ${BASE_URL}" fi } cmd_sync() { CODE="$(curl -s${KFLAG} -o /dev/null -w '%{http_code}' -X POST "${BASE_URL}/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 ${BASE_URL} ?)" >&2 exit 1 ;; esac } case "$CMD" in start) cmd_start ;; stop) cmd_stop ;; restart) cmd_stop; cmd_start ;; status) cmd_status ;; sync) cmd_sync ;; logs) exec docker logs -f "$CONTAINER" ;; *) echo "Usage : $0 [start|stop|restart|status|sync|logs]" >&2 exit 1 ;; esac