Préréglage carte par défaut et serve-webapp.sh compatible Traefik
This commit is contained in:
@ -5,7 +5,7 @@
|
||||
- build webapp légère (Raspberry Pi, déploiement 2 machines — cf. `docs/DEPLOY_WEBAPP.md`): `docker compose -f docker-compose.webapp.yml up -d --build` (image `Dockerfile.webapp`, sans PDAL/GPU)
|
||||
- build générateur de tuiles (machine de traitement): `docker compose -f docker-compose.worker.yml up -d --build` (service `worker`, API pour les webapp distantes)
|
||||
- simulation locale du mode deux machines : `docker compose -f docker-compose.local-2m.yml up -d --build` — worker GPU sur :8974 + webapp légère sur :8973 avec son PROPRE cache `output-webapp/` peuplé à la demande depuis le worker. Permet de rebuild l'interface sans toucher au worker, et réciproquement. Résolution 0,2 m uniquement (GENERATE_RESOLUTIONS).
|
||||
- stack webapp (machine légère): `./serve-webapp.sh [start|stop|restart|status|sync|logs]`, config dans `webapp.env` (modèle `webapp.env.example`, ignoré par git) ; mise à jour = `git pull` puis rebuild image + `restart` (cf. `docs/DEPLOY_WEBAPP.md`). **ATTENTION Pi de prod (192.168.3.10) : la webapp y tourne via `docker compose -f docker-compose.webapp.yml -f docker-compose.webapp.override.yml up -d --build` (labels Traefik `lidar.example.fr` + volume réel `/srv/lidar/output`) — NE PAS utiliser `serve-webapp.sh` là-bas : il recrée le conteneur sans labels (route publique 404) et sur le mauvais volume `./output`.**
|
||||
- stack webapp (machine légère): `./serve-webapp.sh [start|stop|restart|status|sync|logs]`, config dans `webapp.env` (modèle `webapp.env.example`, ignoré par git) ; mise à jour = `git pull` puis `restart` (rebuild inclus). Le script pilote `docker compose` et charge automatiquement `docker-compose.webapp.override.yml` s'il existe — utilisable sur le Pi de prod (192.168.3.10) : labels Traefik `lidar.example.fr` + volume réel `/srv/lidar/output` de l'override préservés.
|
||||
- test all: `./run.sh --test` (rebuild automatique de l'image avant les tests ; en `docker run` direct, rebuild manuellement d'abord)
|
||||
- test file: `docker run --rm lidar-lidar python3 -m pytest -v --pyargs lidar_pipeline.tests.<module>`
|
||||
- test case: `docker run --rm lidar-lidar python3 -m pytest -v --pyargs lidar_pipeline.tests.<module>::<TestClass>::<test_method>`
|
||||
|
||||
@ -103,20 +103,22 @@ avec `docker compose`, le montage équivalent est à décommenter dans
|
||||
|
||||
Trois façons, au choix :
|
||||
|
||||
**a. `serve-webapp.sh` (recommandé)** — script de gestion de la stack
|
||||
(arrière-plan, redémarrage automatique au reboot, sous-commandes). La
|
||||
configuration vit dans `webapp.env` (copie du modèle, non versionné) :
|
||||
**a. `serve-webapp.sh` (recommandé)** — script de gestion de la stack. Il
|
||||
pilote `docker compose` et charge automatiquement
|
||||
`docker-compose.webapp.override.yml` s'il existe : les labels Traefik et le
|
||||
volume réel de l'override sont préservés. La configuration vit dans
|
||||
`webapp.env` (copie du modèle, non versionné) :
|
||||
|
||||
```bash
|
||||
cp webapp.env.example webapp.env
|
||||
nano webapp.env # LIDAR_GENERATION_URL, jeton...
|
||||
./serve-webapp.sh # démarre (build au premier lancement) et attend le serveur
|
||||
nano webapp.env # LIDAR_WEBAPP_HOST (labels), jeton...
|
||||
./serve-webapp.sh # démarre (build + up -d) et attend le serveur
|
||||
```
|
||||
|
||||
Sous-commandes : `stop`, `restart` (relit `webapp.env`), `status` (conteneur
|
||||
+ état du rebuild), `sync` (rebuild + vignettes), `logs`. Port hôte via
|
||||
`WEBAPP_PORT` dans `webapp.env` ou l'environnement (`WEBAPP_PORT=9000
|
||||
./serve-webapp.sh`).
|
||||
Sous-commandes : `stop` (compose down), `restart` (rebuild + recréation),
|
||||
`status` (conteneur + état du rebuild), `sync` (rebuild + vignettes),
|
||||
`logs`. Le port hôte vient du mapping compose ; `WEBAPP_PORT` ne sert plus
|
||||
qu'aux sondes locales quand le conteneur est arrêté.
|
||||
|
||||
**b. `run.sh`** — conteneur au premier plan (Ctrl-C arrête tout) : pratique
|
||||
pour tester, les variables se passent depuis l'environnement hôte, `~/.ssh`
|
||||
@ -244,12 +246,11 @@ Sur le Pi, après un `git pull` :
|
||||
docker compose -f docker-compose.webapp.yml up -d --build # rebuild + redémarrage
|
||||
```
|
||||
|
||||
Avec `serve-webapp.sh`, l'image est à reconstruire explicitement avant de
|
||||
redémarrer (le script ne reconstruit qu'au premier lancement) :
|
||||
Avec `serve-webapp.sh` (l'override Traefik est chargé automatiquement s'il
|
||||
existe) :
|
||||
|
||||
```bash
|
||||
docker build -f Dockerfile.webapp -t lidar-webapp .
|
||||
./serve-webapp.sh restart
|
||||
./serve-webapp.sh restart # rebuild de l'image + recréation du conteneur
|
||||
```
|
||||
|
||||
Avec `run.sh --serve-webapp`, l'image est reconstruite automatiquement à
|
||||
|
||||
@ -205,19 +205,23 @@ VIZ_LEGENDS = {
|
||||
|
||||
# Couches activées par défaut à l'ouverture de la carte : seules celles-ci
|
||||
# sont allumées, toutes les autres sont désactivées (mais restent accessibles
|
||||
# via le panneau). Ordre = pile basse → haute.
|
||||
DEFAULT_LAYERS = ('slope', 'aspect', 'positive_openness')
|
||||
# via le panneau). Ordre = pile basse → haute : une couche opaque (opacité 1)
|
||||
# doit rester en bas pour ne pas masquer les autres.
|
||||
DEFAULT_LAYERS = ('positive_openness', 'aspect')
|
||||
|
||||
# Opacité par défaut des couches allumées (0–1) ; les absentes restent à 1.
|
||||
DEFAULT_OPACITY = {'aspect': 0.6}
|
||||
|
||||
# Couche « principale » (mise en avant ; index_tiles.json + affichage en
|
||||
# direct de la webapp).
|
||||
DEFAULT_VIZ = DEFAULT_LAYERS[0]
|
||||
|
||||
# Restriction du panneau de couches : seule la sélection DEFAULT_LAYERS est
|
||||
# Restriction du panneau de couches : seule la sélection ci-dessous est
|
||||
# proposée (couches conservées ; les autres ne sont plus ni affichées ni
|
||||
# référencées). None = toutes les visualisations présentes sur disque.
|
||||
# Le sélecteur de génération/régénération est piloté par le même registre
|
||||
# (voir _render_html).
|
||||
PANEL_VIZ = DEFAULT_LAYERS
|
||||
# (voir _render_html). La pente y reste proposée mais éteinte par défaut.
|
||||
PANEL_VIZ = ('slope', 'aspect', 'positive_openness')
|
||||
|
||||
# Correspondance mot-clé de fichier de sortie → nom d'étape --only du pipeline
|
||||
# (les trois visualisations dont le nom de sortie diffère du nom d'étape,
|
||||
@ -1118,6 +1122,7 @@ def build_index(output_dir, output_format='avif'):
|
||||
'viz_meta': {k: {'label': VIZ_LABELS.get(k, k)} for k in ordered_viz},
|
||||
'stats': {'default_viz': default_viz,
|
||||
'default_layers': default_layers_present(all_viz_keys),
|
||||
'default_opacity': dict(DEFAULT_OPACITY),
|
||||
'n_tiles': len(display_tiles)},
|
||||
}, ensure_ascii=False), encoding='utf-8')
|
||||
|
||||
@ -1233,6 +1238,7 @@ def _render_html(tiles, global_bbox, all_viz_keys, output_format):
|
||||
stats_json = _json_for_script(
|
||||
{'default_viz': default_viz,
|
||||
'default_layers': default_layers_present(all_viz_keys),
|
||||
'default_opacity': dict(DEFAULT_OPACITY),
|
||||
'n_tiles': len(tiles)},
|
||||
)
|
||||
|
||||
@ -1930,7 +1936,7 @@ _APP_JS = r"""'use strict';
|
||||
// --- Données embarquées par index.py ---
|
||||
// TILES : tuiles affichables (corners WGS84, viz {thumb, full}, meta)
|
||||
// VIZ_META : {clé: {label}} des visualisations disponibles
|
||||
// STATS : {default_viz, n_tiles}
|
||||
// STATS : {default_viz, default_layers, default_opacity, n_tiles}
|
||||
|
||||
const viewport = document.getElementById('viewport');
|
||||
|
||||
@ -1998,9 +2004,26 @@ function defaultLayers() {
|
||||
? STATS.default_layers
|
||||
: ((STATS.default_viz && VIZ_META[STATS.default_viz])
|
||||
? [STATS.default_viz] : [order[0]]);
|
||||
const onSet = new Set(list.filter(k => VIZ_META[k]));
|
||||
const defOrder = list.filter(k => VIZ_META[k]);
|
||||
// La pile respecte l'ordre de default_layers (bas → haut) : une couche
|
||||
// opaque (opacité 1) doit rester sous les semi-transparentes.
|
||||
for (let i = 1; i < defOrder.length; i++) {
|
||||
const bi = order.indexOf(defOrder[i - 1]), ai = order.indexOf(defOrder[i]);
|
||||
if (bi >= 0 && ai >= 0 && bi > ai) {
|
||||
order.splice(bi, 1);
|
||||
order.splice(order.indexOf(defOrder[i]), 0, defOrder[i - 1]);
|
||||
}
|
||||
}
|
||||
// Opacités par défaut (STATS.default_opacity) ; absentes → 1.
|
||||
const defOp = (STATS.default_opacity && typeof STATS.default_opacity === 'object')
|
||||
? STATS.default_opacity : {};
|
||||
const onSet = new Set(defOrder);
|
||||
const on = {}, opacity = {};
|
||||
for (const k of order) { on[k] = onSet.has(k); opacity[k] = 1; }
|
||||
for (const k of order) {
|
||||
on[k] = onSet.has(k);
|
||||
const op = Number(defOp[k]);
|
||||
opacity[k] = Number.isNaN(op) ? 1 : Math.min(1, Math.max(0, op));
|
||||
}
|
||||
return { order, on, opacity, osm: { on: true, opacity: 0.85, dark: true } };
|
||||
}
|
||||
|
||||
|
||||
@ -375,10 +375,17 @@ def test_subtiles_cover_all_layers(tmp_path):
|
||||
|
||||
|
||||
def test_panel_restricted_to_kept_layers():
|
||||
"""Le panneau est restreint aux couches conservées (DEFAULT_LAYERS)."""
|
||||
from lidar_pipeline.index import PANEL_VIZ, DEFAULT_LAYERS, KEYWORD_TO_STEP
|
||||
"""Le panneau est restreint aux couches conservées (PANEL_VIZ).
|
||||
|
||||
Les couches allumées par défaut (DEFAULT_LAYERS) y figurent toutes — le
|
||||
panneau peut en proposer davantage (ex. pente éteinte par défaut), et
|
||||
chaque opacité par défaut vise une couche allumée.
|
||||
"""
|
||||
from lidar_pipeline.index import (PANEL_VIZ, DEFAULT_LAYERS, DEFAULT_OPACITY,
|
||||
KEYWORD_TO_STEP)
|
||||
from lidar_pipeline.pipeline import VIZ_STEPS
|
||||
assert PANEL_VIZ == DEFAULT_LAYERS
|
||||
assert set(DEFAULT_LAYERS) <= set(PANEL_VIZ)
|
||||
assert set(DEFAULT_OPACITY) <= set(DEFAULT_LAYERS)
|
||||
# Chaque couche conservée correspond à une étape --only valide
|
||||
steps = {name for name, _ in VIZ_STEPS}
|
||||
for key in PANEL_VIZ:
|
||||
|
||||
@ -1054,7 +1054,12 @@ def _merge_remote_index():
|
||||
path.write_text(json.dumps({
|
||||
"tiles": merged,
|
||||
"viz_meta": viz_meta,
|
||||
"stats": remote.get("stats") or data.get("stats") or {},
|
||||
# Les stats décrivent les réglages de l'INTERFACE (couches par
|
||||
# défaut) : elles viennent du rebuild local, fait avec le code de
|
||||
# cette webapp — celles du worker ne servent que de repli tant
|
||||
# qu'aucun index local n'existe (sinon une machine mise à jour avant
|
||||
# l'autre afficherait les vieux réglages du worker).
|
||||
"stats": data.get("stats") or remote.get("stats") or {},
|
||||
}, ensure_ascii=False), encoding="utf-8")
|
||||
# Coquille HTML : la liste embarquée doit être la même (fusionnée). Le
|
||||
# JSON ne contient jamais de « ; » : la première séquence « ]; » après
|
||||
|
||||
188
serve-webapp.sh
188
serve-webapp.sh
@ -1,35 +1,39 @@
|
||||
#!/bin/bash
|
||||
# Stack webapp LiDAR — gestion du conteneur carte interactive sur la machine
|
||||
# légère (Raspberry Pi). Cf. docs/DEPLOY_WEBAPP.md.
|
||||
# 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 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)
|
||||
# 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.
|
||||
#
|
||||
# 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://<hôte>:port)
|
||||
# et le bouton ⌖ « centrer sur la position GPS » fonctionne sur téléphone
|
||||
# (accepter l'avertissement « certificat non fiable » une fois).
|
||||
# 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 (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 [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 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"
|
||||
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
|
||||
@ -38,151 +42,109 @@ if [ -f "${SCRIPT_DIR}/webapp.env" ]; then
|
||||
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://<hôte>: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=""
|
||||
COMPOSE_FILES=(-f "${SCRIPT_DIR}/${COMPOSE_BASE}")
|
||||
if [ -f "${SCRIPT_DIR}/${COMPOSE_OVERRIDE}" ]; then
|
||||
COMPOSE_FILES+=(-f "${SCRIPT_DIR}/${COMPOSE_OVERRIDE}")
|
||||
fi
|
||||
BASE_URL="${SCHEME}://127.0.0.1:${WEBAPP_PORT}"
|
||||
|
||||
CMD="${1:-start}"
|
||||
compose() {
|
||||
docker compose "${COMPOSE_FILES[@]}" "$@"
|
||||
}
|
||||
|
||||
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
|
||||
# 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}'
|
||||
}
|
||||
|
||||
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. LIDAR_MOUNT_SSH=0 pour ne PAS l'exposer au conteneur
|
||||
# (durcissement : la webapp sert des requêtes réseau, inutile d'y donner
|
||||
# une clé privée si le git pull distant n'est pas utilisé).
|
||||
SSH_ARGS=()
|
||||
if [ "${LIDAR_MOUNT_SSH:-1}" = "1" ] && [ -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
|
||||
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
|
||||
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 : ${BASE_URL}/"
|
||||
echo " URL : http://127.0.0.1:${port}/"
|
||||
if [ -n "$(lan_ip)" ]; then
|
||||
echo " URL (LAN) : ${SCHEME}://$(lan_ip):${WEBAPP_PORT}/"
|
||||
echo " URL (LAN) : http://$(lan_ip):${port}/"
|
||||
fi
|
||||
if [ "$SCHEME" = "https" ]; then
|
||||
echo " HTTPS (GPS tél.) : actif — sur le téléphone, acceptez l'avertissement « certificat non fiable »"
|
||||
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}"
|
||||
echo " Cache local tuiles : à la demande (LIDAR_GENERATION_URL)"
|
||||
echo " Génération distante: ${LIDAR_GENERATION_URL:-non configurée (override / webapp.env)}"
|
||||
echo "============================================"
|
||||
}
|
||||
|
||||
cmd_stop() {
|
||||
docker rm -f "$CONTAINER" > /dev/null 2>&1 || true
|
||||
echo "Conteneur ${CONTAINER} arrêté."
|
||||
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)."
|
||||
}
|
||||
|
||||
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
|
||||
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 ${BASE_URL}"
|
||||
echo "Webapp : injoignable sur http://127.0.0.1:${port}" >&2
|
||||
fi
|
||||
}
|
||||
|
||||
cmd_sync() {
|
||||
CODE="$(curl -s${KFLAG} -o /dev/null -w '%{http_code}' -X POST "${BASE_URL}/api/sync" || true)"
|
||||
case "$CODE" in
|
||||
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 ${BASE_URL} ?)" >&2
|
||||
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_stop; cmd_start ;;
|
||||
restart) cmd_restart ;;
|
||||
status) cmd_status ;;
|
||||
sync) cmd_sync ;;
|
||||
logs) exec docker logs -f "$CONTAINER" ;;
|
||||
logs) compose logs -f webapp ;;
|
||||
*)
|
||||
echo "Usage : $0 [start|stop|restart|status|sync|logs]" >&2
|
||||
exit 1
|
||||
|
||||
@ -2,7 +2,8 @@
|
||||
# webapp.env est ignoré par git (jetons, chemins locaux).
|
||||
# Syntaxe shell simple : KEY=valeur (les valeurs avec espaces entre guillemets).
|
||||
|
||||
# Port hôte de la carte (défaut 8973)
|
||||
# Port des sondes locales de serve-webapp.sh quand le conteneur est arrêté
|
||||
# (le port publié vient du mapping docker-compose.webapp.yml ; défaut 8973)
|
||||
#WEBAPP_PORT=8973
|
||||
|
||||
# Machine de traitement (service worker de docker-compose.worker.yml).
|
||||
@ -31,12 +32,12 @@
|
||||
# versionné) — cf. docker-compose.webapp.override.yml.example et
|
||||
# docs/DEPLOY_WEBAPP.md. Rien à définir dans ce fichier.
|
||||
#
|
||||
# b. Sans Traefik (chemin serve-webapp.sh) : certificat auto-signé.
|
||||
# b. Sans Traefik : certificat auto-signé monté via l'override compose
|
||||
# local (docker-compose.webapp.override.yml) :
|
||||
# 1. ./make-tls-cert.sh (crée tls/webapp.crt + tls/webapp.key)
|
||||
# 2. ./serve-webapp.sh restart (sert en https://<hôte>:PORT, monte ./tls
|
||||
# et passe ces variables automatiquement). Accepter l'avertissement
|
||||
# « certificat non fiable » sur le téléphone. Ces lignes n'ont d'effet
|
||||
# que si le certificat est stocké ailleurs que dans ./tls :
|
||||
# 2. ajouter à l'override le montage ./tls:/app/tls:ro et les deux
|
||||
# variables ci-dessous, puis ./serve-webapp.sh restart.
|
||||
# Accepter l'avertissement « certificat non fiable » sur le téléphone :
|
||||
#LIDAR_SSL_CERTFILE=/app/tls/webapp.crt
|
||||
#LIDAR_SSL_KEYFILE=/app/tls/webapp.key
|
||||
|
||||
|
||||
Reference in New Issue
Block a user