Resilience : ecran pause, reconnexion WS auto, watchdog systemd, WiFi monitor
- WebSocket : reconnexion avec backoff exponentiel au lieu de reload page - Ecran pause avec tasse de cafe quand le backend est injoignable - Endpoint /api/health pour monitoring externe - Watchdog systemd (sd_notify READY=1 + WATCHDOG=1 toutes les 10s) - Service systemd durci (Type=notify, WatchdogSec=30, KillMode, limites) - WiFi watchdog : script + timer systemd, verifie la gateway toutes les 60s - Destinations : fallback url_tunnel (WireGuard) en priorite Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -37,11 +37,12 @@ def distribuer_photo(chemin_photo: Path, imprimee: bool = False):
|
||||
if dest.get("ftp", False):
|
||||
envoyer_ftp(chemin_photo, dest, sous_dossier)
|
||||
|
||||
# Incrementer le compteur
|
||||
compteur = config.get("compteur", {})
|
||||
if compteur.get("actif", False):
|
||||
compteur["photos_prises"] = compteur.get("photos_prises", 0) + 1
|
||||
mettre_a_jour_config({"compteur": compteur})
|
||||
# Incrementer le compteur (photos imprimees seulement)
|
||||
if imprimee:
|
||||
compteur = config.get("compteur", {})
|
||||
if compteur.get("actif", False):
|
||||
compteur["photos_prises"] = compteur.get("photos_prises", 0) + 1
|
||||
mettre_a_jour_config({"compteur": compteur})
|
||||
|
||||
|
||||
def copier_usb(chemin_photo: Path, chemin_usb: str, sous_dossier: str | None = None):
|
||||
@@ -98,48 +99,44 @@ def envoyer_ftp(chemin_photo: Path, config_dest: dict, sous_dossier: str | None
|
||||
return False
|
||||
|
||||
|
||||
def _upload_booth(url: str, api_key: str, event_id: str, chemin_photo: Path) -> bool:
|
||||
boundary = "----BoothUpload"
|
||||
filename = chemin_photo.name
|
||||
with open(chemin_photo, "rb") as f:
|
||||
file_data = f.read()
|
||||
body = (
|
||||
f"--{boundary}\r\n"
|
||||
f'Content-Disposition: form-data; name="photo"; filename="{filename}"\r\n'
|
||||
f"Content-Type: image/jpeg\r\n\r\n"
|
||||
).encode() + file_data + f"\r\n--{boundary}--\r\n".encode()
|
||||
req = Request(f"{url}/admin/gallery/{event_id}/upload", data=body, method="POST")
|
||||
req.add_header("Content-Type", f"multipart/form-data; boundary={boundary}")
|
||||
req.add_header("X-Api-Key", api_key)
|
||||
with urlopen(req, timeout=10) as resp:
|
||||
return resp.status == 200
|
||||
|
||||
|
||||
def envoyer_booth(chemin_photo: Path, config_booth: dict):
|
||||
"""Envoie une photo vers la galerie live booth."""
|
||||
url = config_booth.get("url", "").rstrip("/")
|
||||
url_tunnel = config_booth.get("url_tunnel", "").rstrip("/")
|
||||
api_key = config_booth.get("api_key", "")
|
||||
event_id = config_booth.get("event_id", "default")
|
||||
config = charger_config()
|
||||
event_id = config.get("evenement", {}).get("event_id") or config_booth.get("event_id", "default")
|
||||
|
||||
if not url:
|
||||
if not url and not url_tunnel:
|
||||
log.warning("Booth non configure (URL manquante)")
|
||||
return False
|
||||
|
||||
try:
|
||||
import mimetypes
|
||||
boundary = "----BoothUpload"
|
||||
filename = chemin_photo.name
|
||||
|
||||
with open(chemin_photo, "rb") as f:
|
||||
file_data = f.read()
|
||||
|
||||
body = (
|
||||
f"--{boundary}\r\n"
|
||||
f'Content-Disposition: form-data; name="photo"; filename="{filename}"\r\n'
|
||||
f"Content-Type: image/jpeg\r\n\r\n"
|
||||
).encode() + file_data + f"\r\n--{boundary}--\r\n".encode()
|
||||
|
||||
req = Request(
|
||||
f"{url}/api/{event_id}/upload",
|
||||
data=body,
|
||||
method="POST",
|
||||
)
|
||||
req.add_header("Content-Type", f"multipart/form-data; boundary={boundary}")
|
||||
req.add_header("X-Api-Key", api_key)
|
||||
|
||||
with urlopen(req, timeout=10) as resp:
|
||||
if resp.status == 200:
|
||||
log.info(f"Photo envoyee au booth : {filename}")
|
||||
for tentative_url in [u for u in (url_tunnel, url) if u]:
|
||||
try:
|
||||
if _upload_booth(tentative_url, api_key, event_id, chemin_photo):
|
||||
log.info(f"Photo envoyee au booth via {tentative_url} : {chemin_photo.name}")
|
||||
return True
|
||||
else:
|
||||
log.error(f"Booth erreur HTTP {resp.status}")
|
||||
return False
|
||||
except (URLError, OSError) as e:
|
||||
log.error(f"Erreur envoi booth : {e}")
|
||||
return False
|
||||
log.error(f"Booth erreur HTTP via {tentative_url}")
|
||||
except (URLError, OSError) as e:
|
||||
log.warning(f"Echec envoi booth via {tentative_url} : {e}")
|
||||
return False
|
||||
|
||||
|
||||
def recuperer_booth_password() -> dict:
|
||||
@@ -147,22 +144,20 @@ def recuperer_booth_password() -> dict:
|
||||
config = charger_config()
|
||||
booth = config.get("booth", {})
|
||||
url = booth.get("url", "").rstrip("/")
|
||||
url_tunnel = booth.get("url_tunnel", "").rstrip("/")
|
||||
api_key = booth.get("api_key", "")
|
||||
event_id = booth.get("event_id", "default")
|
||||
event_id = config.get("evenement", {}).get("event_id") or booth.get("event_id", "default")
|
||||
|
||||
if not url:
|
||||
return {"password": None}
|
||||
|
||||
try:
|
||||
req = Request(f"{url}/api/{event_id}/info")
|
||||
req.add_header("X-Api-Key", api_key)
|
||||
with urlopen(req, timeout=5) as resp:
|
||||
import json
|
||||
data = json.loads(resp.read())
|
||||
return data
|
||||
except (URLError, OSError) as e:
|
||||
log.error(f"Erreur recuperation booth info : {e}")
|
||||
return {"password": None}
|
||||
for tentative_url in [u for u in (url_tunnel, url) if u]:
|
||||
try:
|
||||
req = Request(f"{tentative_url}/admin/gallery/{event_id}/info")
|
||||
req.add_header("X-Api-Key", api_key)
|
||||
with urlopen(req, timeout=5) as resp:
|
||||
import json
|
||||
return json.loads(resp.read())
|
||||
except (URLError, OSError) as e:
|
||||
log.warning(f"Echec recuperation booth info via {tentative_url} : {e}")
|
||||
return {"password": None}
|
||||
|
||||
|
||||
def detecter_usb() -> list[str]:
|
||||
|
||||
Reference in New Issue
Block a user