diff --git a/app/main.py b/app/main.py
index 46617db..df11f87 100644
--- a/app/main.py
+++ b/app/main.py
@@ -595,6 +595,11 @@ def settings_save(
"(défini)" if ntfy_token.strip() else "(vide)",
)
save_notif_config(user_id, ntfy_topic, ntfy_server, rappel_debut_h, rappel_fin_h, ntfy_token)
+ append_log(
+ user_id, "settings", "Réglages ntfy",
+ f"topic={ntfy_topic or '(vide)'} server={ntfy_server} plage={rappel_debut_h}h-{rappel_fin_h}h "
+ f"jeton={'défini' if ntfy_token.strip() else '(vide)'}",
+ )
return RedirectResponse("/settings?saved=1", status_code=303)
@@ -639,6 +644,7 @@ def settings_regenerate_presence_token(request: Request):
new_token = regenerate_presence_token(user_id)
logger.warning("token de présence régénéré pour %r", user_id)
+ append_log(user_id, "settings", "Token présence régénéré", "Les URLs Tasker ont été invalidées et remplacées.")
base_url = str(request.base_url).rstrip("/")
arrivee_url = f"{base_url}/presence/{new_token}/arrivee"
@@ -691,6 +697,13 @@ def settings_plages_save(
logger.info("réglages plages mis à jour: user_id=%r plages=%r", user_id, fields)
save_plages(user_id, fields)
+ append_log(
+ user_id, "settings", "Plages horaires",
+ f"matin {fields['matin_debut']}→{fields['matin_fin']}, "
+ f"aprem {fields['aprem_debut']}→{fields['aprem_fin']}, "
+ f"reprise {fields['pause_dejeuner_fin']}"
+ + (f", visées {fields['arrivee_visee']}→{fields['depart_vise']}" if fields['arrivee_visee'] or fields['depart_vise'] else ""),
+ )
return RedirectResponse("/settings?plages_saved=1", status_code=303)
diff --git a/app/models.py b/app/models.py
index dd9286c..983471e 100644
--- a/app/models.py
+++ b/app/models.py
@@ -272,7 +272,7 @@ def toggle_conge(date: str, type_conge: str, user_id: str = ""):
# ── Journaux (notifications + présence) ───────────────────────────────────────
-LOG_TYPES = ("notif", "presence")
+LOG_TYPES = ("notif", "presence", "settings")
MAX_LOGS = 500
diff --git a/app/templates/logs.html b/app/templates/logs.html
index 1e68289..303ed6e 100644
--- a/app/templates/logs.html
+++ b/app/templates/logs.html
@@ -10,7 +10,7 @@
- {% set counts = {'all': logs|length, 'notif': logs | selectattr('type', 'equalto', 'notif') | list | length, 'presence': logs | selectattr('type', 'equalto', 'presence') | list | length} %}
+ {% set counts = {'all': logs|length, 'notif': logs | selectattr('type', 'equalto', 'notif') | list | length, 'presence': logs | selectattr('type', 'equalto', 'presence') | list | length, 'settings': logs | selectattr('type', 'equalto', 'settings') | list | length} %}
Tout {{ counts.all }}
@@ -20,6 +20,9 @@
Présence {{ counts.presence }}
+
+ Réglages {{ counts.settings }}
+
{% set visible = logs if filter == 'all' else logs | selectattr('type', 'equalto', filter) | list %}
@@ -29,7 +32,7 @@
{% for entry in visible %}
{{ entry.ts }}
-
{{ 'Notification' if entry.type == 'notif' else 'Présence' }}
+
{{ {'notif': 'Notification', 'presence': 'Présence', 'settings': 'Réglages'}[entry.type] }}
{{ entry.event }}
{% if entry.message %}{{ entry.message }}{% endif %}
@@ -105,6 +108,7 @@
}
.log-notif .log-type { color: var(--accent); }
.log-presence .log-type { color: var(--accent-2); }
+.log-settings .log-type { color: var(--conge-txt); }
.log-body { display: flex; flex-direction: column; gap: .15rem; }
.log-event {
font-weight: 500;
diff --git a/app/tests/test_logs.py b/app/tests/test_logs.py
index 37482d7..e065035 100644
--- a/app/tests/test_logs.py
+++ b/app/tests/test_logs.py
@@ -299,3 +299,50 @@ def test_regenerate_preserves_other_notif_fields():
assert after["ntfy_topic"] == "topic-x"
assert after["ntfy_server"] == "https://ntfy.arkel.fr"
assert after["ntfy_token"] == "tk_xyz"
+
+
+# ── Changements de paramètres tracés dans le journal ─────────────────────────
+
+def test_settings_save_logged():
+ """Sauvegarder les réglages ntfy doit écrire une entrée de journal."""
+ models.save_notif_config("alice", ntfy_topic="t", ntfy_server="https://ntfy.sh")
+ before = len([l for l in models.load_logs("alice") if l["type"] == "settings"])
+ client.post("/settings", cookies=COOKIES, data={
+ "ntfy_topic": "topic-new",
+ "ntfy_server": "https://ntfy.arkel.fr",
+ "ntfy_token": "tk_abc",
+ "rappel_debut_h": "8",
+ "rappel_fin_h": "19",
+ })
+ after = [l for l in models.load_logs("alice") if l["type"] == "settings"]
+ assert len(after) == before + 1
+ latest = after[0]
+ assert latest["event"] == "Réglages ntfy"
+ assert "topic-new" in latest["message"]
+ assert "ntfy.arkel.fr" in latest["message"]
+
+
+def test_settings_plages_save_logged():
+ """Sauvegarder les plages horaires doit écrire une entrée de journal."""
+ before = len([l for l in models.load_logs("alice") if l["type"] == "settings"])
+ client.post("/settings/plages", cookies=COOKIES, data={
+ "matin_debut": "09:00", "matin_fin": "12:00",
+ "aprem_debut": "14:00", "aprem_fin": "17:00",
+ "pause_dejeuner_fin": "13:30",
+ "arrivee_visee": "", "depart_vise": "",
+ })
+ after = [l for l in models.load_logs("alice") if l["type"] == "settings"]
+ assert len(after) == before + 1
+ latest = after[0]
+ assert latest["event"] == "Plages horaires"
+ assert "09:00" in latest["message"]
+
+
+def test_regenerate_presence_token_logged():
+ """Régénérer le token de présence doit écrire une entrée de journal."""
+ before = len([l for l in models.load_logs("alice") if l["type"] == "settings"])
+ client.post("/settings/regenerate-presence-token", cookies=COOKIES)
+ after = [l for l in models.load_logs("alice") if l["type"] == "settings"]
+ assert len(after) == before + 1
+ latest = after[0]
+ assert "régénéré" in latest["event"].lower()