feat: auto-vérification des pastilles 404 rouges au chargement (v1.10.0)

- Nouveau endpoint POST /errors/status-batch (jusqu'à 200 chemins)
- Au chargement de la page, tous les dots rouges sont vérifiés en batch
  et passent automatiquement en vert si le fichier existe désormais

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Alpinux 2026-05-06 20:13:33 +02:00
parent 78c59a5141
commit ef255d605f
4 changed files with 49 additions and 4 deletions

View file

@ -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é

View file

@ -1 +1 @@
1.9.1
1.10.0

View file

@ -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()

View file

@ -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)
═══════════════════════════════════════════════ */