264 lines
12 KiB
Bash
Executable File
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}"
|