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
|
# 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
|
## [1.5.1] — 2026-05-06
|
||||||
|
|
||||||
### Corrigé
|
### 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-header h2 { margin: 0; }
|
||||||
.err-total-badge { font-size: .75rem; font-weight: 400; color: var(--muted); margin-left: .5rem; }
|
.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; }
|
.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; }
|
.err-table .err-path code { font-size: .82rem; color: var(--text); word-break: break-all; }
|
||||||
.col-err-status { width: 1.5rem; text-align: center; }
|
.col-err-status { width: 1.5rem; text-align: center; }
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,10 @@
|
||||||
{% if not entries %}
|
{% if not entries %}
|
||||||
<p style="color:var(--muted); margin-top:.5rem">Aucune erreur 404 dans les logs récents.</p>
|
<p style="color:var(--muted); margin-top:.5rem">Aucune erreur 404 dans les logs récents.</p>
|
||||||
{% else %}
|
{% 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">
|
<table class="file-table err-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
|
|
@ -31,7 +35,7 @@
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for path, info in entries %}
|
{% 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">
|
<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 %}"
|
<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 }}"
|
data-path="{{ path }}"
|
||||||
|
|
@ -80,6 +84,32 @@
|
||||||
const IGNORE_URL = {{ url_for('errors_ignore') | tojson }};
|
const IGNORE_URL = {{ url_for('errors_ignore') | tojson }};
|
||||||
const BAN_URL = {{ url_for('errors_ban') | 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 ── */
|
/* ── Expand/collapse detail ── */
|
||||||
let openRow = null;
|
let openRow = null;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -48,8 +48,8 @@ if $DRY_RUN; then
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
ssh "$REMOTE_HOST" "sudo mkdir -p $REMOTE_DEST && sudo chown static-cdn:static-cdn $REMOTE_DEST"
|
ssh "$REMOTE_HOST" "sudo mkdir -p $REMOTE_DEST"
|
||||||
rsync "${RSYNC_OPTS[@]}" "$APP_DIR/" "$REMOTE_HOST:$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"
|
ssh "$REMOTE_HOST" "sudo chown -R static-cdn:static-cdn $REMOTE_DEST"
|
||||||
echo -e " ${GREEN}✓ Fichiers copiés${RESET}"
|
echo -e " ${GREEN}✓ Fichiers copiés${RESET}"
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue