Administration Docker à la demande : profil admin, arrêt après 2 h d'inactivité, page « Administration fermée »

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Mf2kATMtqVABHSYdxJMnim
This commit is contained in:
FabriceandClaude Opus 5.5 committed 2026-10-10 07:58:04 +00:00
1 parent 02473fafbf
commit daaee62373
6 files changed
+49 -10

No files matched your search

+3 -1
View File
@@ -58,6 +58,8 @@ def main(argv=None):
help="derrière le serveur web du site (mot de passe obligatoire)")
i.add_argument("--prefixe", default="", help="chemin de l'interface sur le site, par exemple /admin")
i.add_argument("--ecoute", help="adresse d'écoute (défaut 127.0.0.1 ; 0.0.0.0 si le serveur web est ailleurs)")
i.add_argument("--arret-inactif", type=float, default=0, metavar="MINUTES",
help="s'arrêter après MINUTES sans activité (0 = jamais)")
mp = sp.add_parser("mot-de-passe", help="définir ou supprimer le mot de passe de l'interface")
mp.add_argument("--supprimer", action="store_true")
@@ -119,7 +121,7 @@ def main(argv=None):
sys.exit("❌ --certificat et --cle vont ensemble.")
lancer(racine, args.port, ouvrir=not (args.sans_navigateur or args.proxy), distant=args.distant,
certificat=args.certificat, cle=args.cle, proxy=args.proxy, prefixe=args.prefixe,
ecoute=args.ecoute)
ecoute=args.ecoute, arret_inactif=args.arret_inactif)
except KeyboardInterrupt:
print("\nInterrompu.")
sys.exit(130)
+24 -2
View File
@@ -26,6 +26,7 @@ import signal
import ssl
import string
import threading
import time
import webbrowser
from contextlib import redirect_stdout
from datetime import date, timedelta
@@ -107,6 +108,7 @@ class Application:
self.prefixe = ("/" + prefixe.strip("/")) if prefixe.strip("/") else ""
ENV.globals["base"] = self.prefixe
self._cache = {} # voyage → (empreinte des sources, analyse)
self.derniere_activite = time.time()
self.routes = []
for methode, motif, fn in [
("GET", r"/", self.accueil),
@@ -674,6 +676,10 @@ def creer_gestionnaire(app, port, gardien=None):
return self.client_address[0]
def traiter(self, methode, tete=False):
# Activité, pour l'arrêt automatique : les relèves périodiques d'un
# onglet resté ouvert ne comptent pas.
if not self.path.split("?")[0].endswith(("/tache/bandeau", "/tache/fragment")):
app.derniere_activite = time.time()
try:
if not (gardien.distant or gardien.proxy) and self.headers.get("Host") not in hotes:
raise Erreur(403, "Hôte refusé")
@@ -861,14 +867,15 @@ def _echapper(s):
def lancer(racine, port=8765, ouvrir=True, distant=False, certificat=None, cle=None,
proxy=False, prefixe="", ecoute=None):
proxy=False, prefixe="", ecoute=None, arret_inactif=0):
"""Lance l'interface.
distant : écoute sur toutes les interfaces réseau, mot de passe obligatoire ;
certificat, cle : HTTPS ;
proxy : derrière le serveur web du site (qui transmet https://…/admin/ à
127.0.0.1:port), mot de passe obligatoire ; prefixe : « /admin » ;
ecoute : adresse d'écoute imposée (« 0.0.0.0 » si le serveur web est sur
une autre machine)."""
une autre machine) ; arret_inactif : minutes sans activité (ni traitement en
cours) au bout desquelles l'interface s'arrête d'elle-même, 0 = jamais."""
hors_machine = ecoute and ecoute not in ("127.0.0.1", "localhost", "::1")
if (distant or proxy or hors_machine) and not acces.defini():
raise SystemExit("❌ L'accès depuis le réseau demande un mot de passe : lancez d'abord « rtg mot-de-passe ».")
@@ -915,6 +922,21 @@ def lancer(racine, port=8765, ouvrir=True, distant=False, certificat=None, cle=N
def sur_sigterm(*_):
raise KeyboardInterrupt
signal.signal(signal.SIGTERM, sur_sigterm)
if arret_inactif:
print(f" Arrêt automatique après {arret_inactif:g} min sans activité.")
def surveiller():
while True:
time.sleep(min(30, arret_inactif * 30))
t = taches.COURANTE
if t and t.en_cours:
app.derniere_activite = time.time()
elif time.time() - app.derniere_activite > arret_inactif * 60:
print(f"💤 Aucune activité depuis {arret_inactif:g} min : arrêt de l'interface.")
serveur.shutdown()
return
threading.Thread(target=surveiller, daemon=True, name="rtg-veille").start()
try:
serveur.serve_forever()
except KeyboardInterrupt: