feat(erreurs): recherche dynamique par chemin ou IP (#42)
Champ de filtre en temps réel au-dessus du tableau des 404 ; ferme le panneau de détail des lignes masquées. Corrige aussi rsync via sudo pour préserver les droits static-cdn. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
abf13db0e6
commit
3a6f363e1d
5 changed files with 45 additions and 4 deletions
|
|
@ -1,5 +1,12 @@
|
|||
# Changelog — Alpinux Static
|
||||
|
||||
## [1.5.2] — 2026-05-06
|
||||
|
||||
### Ajouté
|
||||
- Erreurs 404 : champ de recherche dynamique — filtre les lignes par chemin ou adresse IP à chaque frappe, avec compteur de résultats
|
||||
|
||||
---
|
||||
|
||||
## [1.5.1] — 2026-05-06
|
||||
|
||||
### Corrigé
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
1.5.1
|
||||
1.5.2
|
||||
|
|
|
|||
|
|
@ -326,6 +326,10 @@ footer { background: var(--blue-dark); color: rgba(255,255,255,.6); margin-top:
|
|||
.err-header h2 { margin: 0; }
|
||||
.err-total-badge { font-size: .75rem; font-weight: 400; color: var(--muted); margin-left: .5rem; }
|
||||
.btn-sm { font-size: .8rem; padding: .3rem .7rem; background: var(--blue-light); color: var(--blue); border: 1px solid var(--border); border-radius: 6px; cursor: pointer; }
|
||||
.err-search-wrap { display: flex; align-items: center; gap: .75rem; margin-bottom: .75rem; }
|
||||
.err-search { flex: 1; max-width: 380px; padding: .4rem .75rem; border: 1px solid var(--border); border-radius: 8px; font-size: .9rem; background: var(--bg); color: var(--text); }
|
||||
.err-search:focus { outline: none; border-color: var(--blue); box-shadow: 0 0 0 2px var(--blue-light); }
|
||||
.err-search-count { font-size: .8rem; color: var(--muted); }
|
||||
|
||||
.err-table .err-path code { font-size: .82rem; color: var(--text); word-break: break-all; }
|
||||
.col-err-status { width: 1.5rem; text-align: center; }
|
||||
|
|
|
|||
|
|
@ -18,6 +18,10 @@
|
|||
{% if not entries %}
|
||||
<p style="color:var(--muted); margin-top:.5rem">Aucune erreur 404 dans les logs récents.</p>
|
||||
{% else %}
|
||||
<div class="err-search-wrap">
|
||||
<input type="search" id="err-search" class="err-search" placeholder="Filtrer par chemin ou adresse IP…" autocomplete="off">
|
||||
<span class="err-search-count" id="err-search-count"></span>
|
||||
</div>
|
||||
<table class="file-table err-table">
|
||||
<thead>
|
||||
<tr>
|
||||
|
|
@ -31,7 +35,7 @@
|
|||
</thead>
|
||||
<tbody>
|
||||
{% for path, info in entries %}
|
||||
<tr class="err-row" data-path="{{ path }}">
|
||||
<tr class="err-row" data-path="{{ path }}" data-ips="{{ info.ips.keys() | join(' ') }}">
|
||||
<td class="col-err-status">
|
||||
<span class="err-status-dot err-status-dot--{% if not info.last_seen %}unk{% elif (info.ips | length) > 0 %}active{% else %}ok{% endif %}"
|
||||
data-path="{{ path }}"
|
||||
|
|
@ -80,6 +84,32 @@
|
|||
const IGNORE_URL = {{ url_for('errors_ignore') | tojson }};
|
||||
const BAN_URL = {{ url_for('errors_ban') | tojson }};
|
||||
|
||||
/* ── Search / filter ── */
|
||||
const searchInput = document.getElementById('err-search');
|
||||
const searchCount = document.getElementById('err-search-count');
|
||||
const allRows = [...document.querySelectorAll('.err-row')];
|
||||
|
||||
function applyFilter() {
|
||||
const q = searchInput.value.trim().toLowerCase();
|
||||
let visible = 0;
|
||||
allRows.forEach(row => {
|
||||
const match = !q
|
||||
|| row.dataset.path.toLowerCase().includes(q)
|
||||
|| row.dataset.ips.toLowerCase().includes(q);
|
||||
const detailRow = row.nextElementSibling;
|
||||
row.style.display = match ? '' : 'none';
|
||||
if (!match && detailRow && detailRow.classList.contains('err-detail-row')) {
|
||||
detailRow.style.display = 'none';
|
||||
row.querySelector('.btn-detail').textContent = '▼';
|
||||
if (openRow === detailRow) openRow = null;
|
||||
}
|
||||
if (match) visible++;
|
||||
});
|
||||
searchCount.textContent = q ? `${visible} / ${allRows.length}` : '';
|
||||
}
|
||||
|
||||
if (searchInput) searchInput.addEventListener('input', applyFilter);
|
||||
|
||||
/* ── Expand/collapse detail ── */
|
||||
let openRow = null;
|
||||
|
||||
|
|
|
|||
|
|
@ -48,8 +48,8 @@ if $DRY_RUN; then
|
|||
exit 0
|
||||
fi
|
||||
|
||||
ssh "$REMOTE_HOST" "sudo mkdir -p $REMOTE_DEST && sudo chown static-cdn:static-cdn $REMOTE_DEST"
|
||||
rsync "${RSYNC_OPTS[@]}" "$APP_DIR/" "$REMOTE_HOST:$REMOTE_DEST/"
|
||||
ssh "$REMOTE_HOST" "sudo mkdir -p $REMOTE_DEST"
|
||||
rsync "${RSYNC_OPTS[@]}" --rsync-path="sudo rsync" "$APP_DIR/" "$REMOTE_HOST:$REMOTE_DEST/"
|
||||
ssh "$REMOTE_HOST" "sudo chown -R static-cdn:static-cdn $REMOTE_DEST"
|
||||
echo -e " ${GREEN}✓ Fichiers copiés${RESET}"
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue