adsb-1090/update-webapp.sh

264 lines
12 KiB
Bash
Executable File

#!/bin/bash
# ═══════════════════════════════════════════════════════════════════════════════
# update-webapp.sh — Rebuild et redéploiement de la webapp ADS-B
# Usage : ./update-webapp.sh [--no-cache] [--check-only]
# ═══════════════════════════════════════════════════════════════════════════════
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
cd "$SCRIPT_DIR"
# ── Fichiers d'environnement (v4.2) ──────────────────────────────────────────
# Depuis les profiles Compose, un « docker compose » nu affiche
# « The "RX1_SERIAL" variable is not set » à chaque appel. Inoffensif pour la
# webapp (aucun profile), mais le bruit masque les vraies erreurs — et si
# PG_PASSWORD n'est pas injecté, le conteneur démarre sans accès à la base.
COMPOSE_ARGS=()
[[ -f "$SCRIPT_DIR/.env" ]] && COMPOSE_ARGS+=(--env-file "$SCRIPT_DIR/.env")
[[ -f "$SCRIPT_DIR/.env.receivers" ]] && COMPOSE_ARGS+=(--env-file "$SCRIPT_DIR/.env.receivers")
dc() { docker compose "${COMPOSE_ARGS[@]}" "$@"; }
# ── Couleurs ──────────────────────────────────────────────────────────────────
GREEN='\033[0;32m'; RED='\033[0;31m'; YELLOW='\033[1;33m'
CYAN='\033[0;36m'; BOLD='\033[1m'; NC='\033[0m'
ok() { echo -e " ${GREEN}${NC} $1"; }
fail() { echo -e " ${RED}${NC} $1"; }
warn() { echo -e " ${YELLOW}${NC} $1"; }
info() { echo -e " ${CYAN}${NC} $1"; }
hdr() { echo -e "\n${BOLD}$1${NC}"; }
NO_CACHE=""
CHECK_ONLY=0
for arg in "$@"; do
case "$arg" in
--no-cache) NO_CACHE="--no-cache" ;;
--check-only) CHECK_ONLY=1 ;;
esac
done
echo -e "${BOLD}╔══════════════════════════════════════════════════════╗${NC}"
echo -e "${BOLD}║ Déploiement webapp ADS-B Linux-25 ║${NC}"
echo -e "${BOLD}$(date '+%a %d/%m/%Y %H:%M:%S')${NC}"
echo -e "${BOLD}╚══════════════════════════════════════════════════════╝${NC}"
# ── 1. Prérequis ──────────────────────────────────────────────────────────────
hdr "1. Vérification des prérequis"
# Docker disponible ?
if ! docker info &>/dev/null; then
fail "Docker non disponible — arrêt"
exit 1
fi
ok "Docker disponible ($(docker version --format '{{.Server.Version}}' 2>/dev/null))"
# Dockerfile présent ?
if [ ! -f "$SCRIPT_DIR/webapp/Dockerfile" ]; then
fail "webapp/Dockerfile introuvable dans $SCRIPT_DIR"
exit 1
fi
ok "Dockerfile présent : $SCRIPT_DIR/webapp/Dockerfile"
# Sources présentes ?
SRC_COUNT=$(find "$SCRIPT_DIR/webapp/src" -type f 2>/dev/null | wc -l)
if [ "$SRC_COUNT" -eq 0 ]; then
fail "Aucun fichier source dans webapp/src/"
exit 1
fi
ok "$SRC_COUNT fichiers sources dans webapp/src/"
# ── 2. Accès internet ─────────────────────────────────────────────────────────
hdr "2. Vérification accès internet"
INTERNET=0
URLS_TO_CHECK=(
"https://cdn.jsdelivr.net/"
"https://unpkg.com/"
)
for url in "${URLS_TO_CHECK[@]}"; do
CODE=$(curl -s --max-time 5 -o /dev/null -w "%{http_code}" "$url" 2>/dev/null || echo "000")
if [ "$CODE" != "000" ] && [ "$CODE" != "599" ]; then
ok "Accès internet OK ($url → HTTP $CODE)"
INTERNET=1
break
else
warn "Pas de réponse : $url"
fi
done
if [ "$INTERNET" -eq 0 ]; then
warn "Aucun accès internet détecté"
# Vérifier si les assets sont déjà dans l'image existante
EXISTING_ASSETS=$(docker exec adsb-webapp ls /var/www/html/assets/ 2>/dev/null | wc -l || echo 0)
if [ "$EXISTING_ASSETS" -gt 0 ]; then
warn "Assets déjà présents dans le conteneur ($EXISTING_ASSETS fichiers)"
warn "Le build va échouer si l'image doit être recréée depuis zéro"
warn "Option : copier uniquement les sources sans rebuild"
echo ""
echo -e " ${YELLOW}Que voulez-vous faire ?${NC}"
echo " [1] Tenter le rebuild quand même (peut échouer sans internet)"
echo " [2] Copier uniquement les sources dans le conteneur existant"
echo " [3] Annuler"
read -rp " Choix [1/2/3] : " CHOIX
case "$CHOIX" in
2)
hdr "Copie des sources dans le conteneur existant"
for f in "$SCRIPT_DIR/webapp/src/"*; do
fname=$(basename "$f")
if [ -d "$f" ]; then
docker cp "$f" "adsb-webapp:/var/www/html/$fname" && \
ok "Copié : $fname/" || warn "Échec : $fname/"
else
docker cp "$f" "adsb-webapp:/var/www/html/$fname" && \
ok "Copié : $fname" || warn "Échec : $fname"
fi
done
docker exec adsb-webapp chown -R www-data:www-data /var/www/html/ 2>/dev/null || true
ok "Sources mises à jour sans rebuild"
exit 0
;;
3)
info "Annulé"
exit 0
;;
*)
warn "Tentative de rebuild sans garantie de succès"
;;
esac
else
fail "Pas d'internet et aucun conteneur existant — impossible de builder"
exit 1
fi
fi
[ "$CHECK_ONLY" -eq 1 ] && { info "Mode --check-only : arrêt ici"; exit 0; }
# ── 3. Build de l'image ───────────────────────────────────────────────────────
hdr "3. Build de l'image webapp"
info "Lancement du build${NO_CACHE:+ (--no-cache)}..."
BUILD_START=$(date +%s)
if dc build $NO_CACHE webapp 2>&1 | tee /tmp/webapp-build.log | \
grep -E "^(Step|#[0-9]|Successfully|ERROR|error)" ; then
BUILD_END=$(date +%s)
ok "Build réussi en $((BUILD_END - BUILD_START))s"
else
BUILD_END=$(date +%s)
fail "Build échoué après $((BUILD_END - BUILD_START))s"
echo ""
warn "Dernières lignes du log :"
tail -20 /tmp/webapp-build.log
exit 1
fi
# ── 4. Redémarrage du conteneur ───────────────────────────────────────────────
hdr "4. Redémarrage du conteneur"
info "Arrêt de adsb-webapp..."
dc stop webapp
dc rm -f webapp
info "Démarrage avec la nouvelle image..."
dc up -d webapp
sleep 8
# ── 5. Vérification post-déploiement ─────────────────────────────────────────
hdr "5. Vérification post-déploiement"
# Conteneur running ?
STATUS=$(docker inspect --format '{{.State.Status}}' adsb-webapp 2>/dev/null || echo "absent")
[ "$STATUS" = "running" ] && ok "Conteneur adsb-webapp : running" \
|| fail "Conteneur adsb-webapp : $STATUS"
# Port 8080 en écoute ?
ss -tlnp 2>/dev/null | grep -q ":8080" \
&& ok "Port 8080 en écoute" \
|| fail "Port 8080 absent"
# HTTP 200 ?
HTTP=$(curl -s -o /dev/null -w "%{http_code}" --max-time 5 "http://127.0.0.1:8080/" 2>/dev/null)
[ "$HTTP" = "200" ] && ok "HTTP 200 sur http://127.0.0.1:8080/" \
|| fail "HTTP $HTTP sur http://127.0.0.1:8080/"
# Nombre d'onglets ?
TABS=$(docker exec adsb-webapp grep -c 'href="#tab' /var/www/html/index.php 2>/dev/null || echo 0)
[ "$TABS" -ge 5 ] && ok "$TABS onglets détectés dans index.php" \
|| warn "$TABS onglets détectés (< 5 attendus)"
# Taille app.js ?
JS_SZ=$(docker exec adsb-webapp wc -c /var/www/html/js/app.js 2>/dev/null | awk '{print $1}')
info "app.js : ${JS_SZ:-?} octets"
# ── Contrôles fonctionnels de l'API (v4.2) ───────────────────────────────────
# Le fuseau doit être ancré : sans date_default_timezone_set, PHP retombe sur
# UTC et la carte historique interroge une fenêtre décalée de 2 h — elle
# revient alors systématiquement vide, sans la moindre erreur HTTP.
if docker exec adsb-webapp grep -q "date_default_timezone_set" /var/www/html/api.php 2>/dev/null; then
ok "api.php : fuseau horaire ancré (date_default_timezone_set)"
else
warn "api.php : date_default_timezone_set absent — carte historique probablement vide (décalage UTC/CEST)"
fi
for act in live kpi trajectories; do
BODY=$(curl -s --max-time 10 "http://127.0.0.1:8080/api.php?action=${act}" 2>/dev/null)
if echo "$BODY" | head -c 1 | grep -q '{'; then
ok "api.php?action=${act} : JSON valide"
else
warn "api.php?action=${act} : réponse inattendue"
fi
done
# La carte historique est le seul endpoint sensible au fuseau : on l'interroge
# sur une fenêtre récente, qui doit contenir des données si le stack reçoit.
HIST_FROM=$(date -d '-40 minutes' '+%Y-%m-%d %H:%M:%S')
HIST=$(curl -s --max-time 20 --get \
--data-urlencode "action=trajectories_history" \
--data-urlencode "from=${HIST_FROM}" \
--data-urlencode "window=30" \
"http://127.0.0.1:8080/api.php" 2>/dev/null)
HIST_COUNT=$(echo "$HIST" | grep -oP '"count"\s*:\s*\K[0-9]+' | head -1)
if [ "${HIST_COUNT:-0}" -gt 0 ]; then
ok "Carte historique : ${HIST_COUNT} avion(s) sur les 30 dernières minutes"
else
warn "Carte historique : 0 avion sur une fenêtre récente — vérifier le fuseau (api.php) ou l'ingestion adsb2pg"
fi
# Assets présents ?
ASSETS=$(docker exec adsb-webapp ls /var/www/html/assets/ 2>/dev/null | wc -l)
ok "$ASSETS fichiers assets dans /var/www/html/assets/"
# Drapeaux : sans eux, tous les pays s'affichent avec le pictogramme « ? ».
# Le Dockerfile les télécharge, mais un build sans réseau les laisse absents.
FLAG_N=$(docker exec adsb-webapp sh -c 'ls /var/www/html/assets/flags/*.png 2>/dev/null | wc -l' 2>/dev/null)
if [ "${FLAG_N:-0}" -gt 50 ]; then
ok "$FLAG_N drapeau(x) disponible(s)"
else
warn "Seulement ${FLAG_N:-0} drapeau(x) — les pays afficheront un pictogramme générique"
warn "Correctif : /data/adsb/scripts/fetch_flags.sh puis relancer ce script"
fi
# Endpoints d'analyse des départs
for act in airports departures_offfield_zones; do
BODY=$(curl -s --max-time 25 "http://127.0.0.1:8080/api.php?action=${act}&window=1440" 2>/dev/null)
if echo "$BODY" | grep -q '"error"'; then
warn "api.php?action=${act} : $(echo "$BODY" | head -c 110)"
elif echo "$BODY" | head -c 1 | grep -q '{'; then
ok "api.php?action=${act} : JSON valide"
else
warn "api.php?action=${act} : réponse inattendue"
fi
done
# IP pour le lien final
IP=$(ip -4 addr show 2>/dev/null | grep -oP '(?<=inet )192\.[0-9.]+' | head -1)
[ -z "$IP" ] && IP=$(hostname -I | awk '{print $1}')
echo ""
echo -e "${BOLD}╔══════════════════════════════════════════════════════╗${NC}"
echo -e "${BOLD}║ ✓ Déploiement terminé ║${NC}"
echo -e "${BOLD}║ Dashboard : http://${IP}:8080${NC}${BOLD}${NC}"
echo -e "${BOLD}╚══════════════════════════════════════════════════════╝${NC}"