diff --git a/app/CHANGELOG.md b/app/CHANGELOG.md index 8fe2c86..35bed35 100644 --- a/app/CHANGELOG.md +++ b/app/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog — Alpinux Static +## [1.10.0] — 2026-05-06 + +### Modifié +- Erreurs 404 : les pastilles de statut (●) rouges sont désormais **vérifiées automatiquement** au chargement de la page — elles passent en vert si le fichier existe maintenant, sans nécessiter un clic +- Nouveau endpoint `POST /errors/status-batch` : vérifie jusqu'à 200 chemins en une seule requête (simple `os.path.exists` côté serveur) + +--- + ## [1.9.1] — 2026-05-06 ### Corrigé diff --git a/app/VERSION b/app/VERSION index 9ab8337..81c871d 100644 --- a/app/VERSION +++ b/app/VERSION @@ -1 +1 @@ -1.9.1 +1.10.0 diff --git a/app/app.py b/app/app.py index 656b284..b35e3f7 100644 --- a/app/app.py +++ b/app/app.py @@ -1218,6 +1218,19 @@ def errors_detail(): }) +@app.route("/errors/status-batch", methods=["POST"]) +def errors_status_batch(): + redir = _require_admin() + if redir: + return jsonify({"error": "not authorized"}), 403 + paths = request.get_json(silent=True, force=True) or {} + paths = paths.get("paths", []) + if not isinstance(paths, list): + return jsonify({"error": "invalid"}), 400 + results = {p: _is_still_404(p) for p in paths[:200]} + return jsonify({"results": results}) + + @app.route("/errors/asinfo") def errors_asinfo(): redir = _require_admin() diff --git a/app/templates/errors_404.html b/app/templates/errors_404.html index d6459a6..c6827ca 100644 --- a/app/templates/errors_404.html +++ b/app/templates/errors_404.html @@ -265,17 +265,41 @@ }); }); + const STATUS_BATCH_URL = '{{ url_for("errors_status_batch") }}'; + + function applyDotResult(dot, still_404) { + dot.textContent = '●'; + dot.className = 'err-status-dot err-status-dot--' + (still_404 ? 'active' : 'ok'); + dot.title = still_404 ? 'Toujours actif' : 'Résolu'; + } + document.querySelectorAll('.err-status-dot').forEach(dot => { dot.style.cursor = 'pointer'; dot.addEventListener('click', async () => { dot.textContent = '○'; dot.className = 'err-status-dot'; const r = await fetch(DETAIL_URL + '?path=' + encodeURIComponent(dot.dataset.path)).then(r => r.json()); - dot.textContent = '●'; - dot.className = 'err-status-dot err-status-dot--' + (r.still_404 ? 'active' : 'ok'); - dot.title = r.still_404 ? 'Toujours actif' : 'Résolu'; + applyDotResult(dot, r.still_404); }); }); + /* Auto-vérification au chargement : tous les dots actifs (rouges) */ + (async () => { + const activeDots = Array.from(document.querySelectorAll('.err-status-dot--active')); + if (!activeDots.length) return; + activeDots.forEach(d => { d.textContent = '○'; d.className = 'err-status-dot'; }); + try { + const paths = activeDots.map(d => d.dataset.path); + const data = await fetch(STATUS_BATCH_URL, { + method: 'POST', + headers: {'Content-Type': 'application/json'}, + body: JSON.stringify({paths}) + }).then(r => r.json()); + activeDots.forEach(dot => applyDotResult(dot, data.results[dot.dataset.path] !== false)); + } catch { + activeDots.forEach(d => { d.textContent = '●'; d.className = 'err-status-dot err-status-dot--active'; }); + } + })(); + /* ═══════════════════════════════════════════════ ONGLET BANNISSEMENTS (chargement AJAX) ═══════════════════════════════════════════════ */