L'iPhone n'est joignable depuis nulle part - CGNAT operateur en 4G, NAT de la box sur le WiFi du studio, UPnP indisponible. Le serveur RTSP de l'application camera est donc inutilisable de l'exterieur. Seule une connexion sortante traverse les deux NAT : c'est le telephone qui pousse. - MediaMTX en conteneur : SRT/RTMP en entree, WebRTC (WHEP) et HLS en sortie. La video ne traverse jamais la JVM - 800 kbps continus dans Spring Boot couteraient un thread par spectateur sans rien apporter. - SRT privilegie sur RTMP : RTMP est du TCP, un paquet perdu sur un lien mobile bloque tout le flux. SRT retransmet selectivement en UDP. - Lecture publique (choix assume), publication authentifiee : sans ca n'importe qui pousserait sa propre video sur le flux "biblio". - Console : client WHEP maison avec repli HLS automatique, et mise en pause quand l'onglet passe en arriere-plan. Correction d'un bug trouve en deployant : le @ExceptionHandler(Exception) avalait les exceptions que Spring sait deja traduire, si bien que toute URL inconnue renvoyait 500 au lieu de 404 - ce qui noyait les vraies pannes au milieu des 500 provoques par les crawlers. Test de regression ajoute. 35 tests au vert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
63 lines
2.3 KiB
Bash
Executable file
63 lines
2.3 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
#
|
|
# Redeploiement sur le serveur Hetzner.
|
|
#
|
|
# ./deploy/deploy.sh
|
|
#
|
|
# Le build tourne sur le serveur, dans le conteneur Maven : la machine de dev
|
|
# n'a pas besoin d'un JDK 21, et l'architecture (arm64 ici, x86_64 la-bas) ne
|
|
# pose pas de probleme.
|
|
|
|
set -euo pipefail
|
|
|
|
HOST="${DEPLOY_HOST:-hetzner}"
|
|
REMOTE_DIR="${DEPLOY_DIR:-/opt/iot-alarm-system}"
|
|
LOCAL_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
|
|
echo "==> Synchronisation vers ${HOST}:${REMOTE_DIR}"
|
|
# --delete sans toucher au .env : il contient le mot de passe de la base et
|
|
# n'existe que sur le serveur.
|
|
rsync -az --delete \
|
|
--exclude 'target/' \
|
|
--exclude '.git/' \
|
|
--exclude '.env' \
|
|
--exclude '.DS_Store' \
|
|
"${LOCAL_DIR}/" "${HOST}:${REMOTE_DIR}/"
|
|
|
|
echo "==> Rendu de la configuration MediaMTX"
|
|
# Le mot de passe de publication vit dans le .env du serveur et n'existe nulle
|
|
# part dans le depot. Le fichier rendu est gitignore.
|
|
ssh "${HOST}" "cd ${REMOTE_DIR} && \
|
|
PASS=\$(grep '^STREAM_PUBLISH_PASSWORD=' .env | cut -d= -f2-) && \
|
|
PUBLIC_IP=\$(curl -s --max-time 10 https://api.ipify.org) && \
|
|
sed -e \"s|__PUBLISH_PASSWORD__|\${PASS}|\" \
|
|
-e \"s|__PUBLIC_IP__|\${PUBLIC_IP}|\" \
|
|
deploy/mediamtx/mediamtx.yml.template > deploy/mediamtx/mediamtx.yml && \
|
|
chmod 600 deploy/mediamtx/mediamtx.yml"
|
|
|
|
echo "==> Build de l'image (tests unitaires + ArchUnit inclus)"
|
|
ssh "${HOST}" "cd ${REMOTE_DIR} && docker compose build app"
|
|
|
|
echo "==> Redemarrage"
|
|
ssh "${HOST}" "cd ${REMOTE_DIR} && docker compose up -d"
|
|
# `up -d` ne recree pas un conteneur dont seule la configuration montee a
|
|
# change : MediaMTX garderait l'ancienne en memoire.
|
|
ssh "${HOST}" "cd ${REMOTE_DIR} && docker compose restart mediamtx"
|
|
|
|
echo "==> Attente du healthcheck"
|
|
for i in $(seq 1 30); do
|
|
status=$(ssh "${HOST}" "docker inspect -f '{{.State.Health.Status}}' iot-alarm-app 2>/dev/null || echo absent")
|
|
if [ "${status}" = "healthy" ]; then
|
|
echo "==> Application saine"
|
|
ssh "${HOST}" "docker exec iot-alarm-app wget -qO- http://localhost:8080/api/v1/alarm/status"
|
|
echo
|
|
exit 0
|
|
fi
|
|
printf '.'
|
|
sleep 5
|
|
done
|
|
|
|
echo
|
|
echo "!! L'application n'est pas devenue saine. Logs :" >&2
|
|
ssh "${HOST}" "cd ${REMOTE_DIR} && docker compose logs --tail 60 app" >&2
|
|
exit 1
|