feat: alerts view + created-at timestamps on decisions
All checks were successful
Deploy / deploy (push) Successful in 22s

Mirror cscli alerts list: new /alerts endpoint hits LAPI machine auth
with since/ip/scenario/origin filters, renders ID, scope:value, reason,
country, AS, events, decisions, created_at.

Decisions table gains a Created column derived from until - duration
(LAPI does not expose created_at on /v1/decisions). Both views format
timestamps locally and append a relative "x ago" via Intl.RelativeTimeFormat,
refreshed every 30s on the decisions view.
This commit is contained in:
2026-06-21 15:13:14 +02:00
parent 13e672c18d
commit 708f6373f1
4 changed files with 242 additions and 3 deletions

View File

@@ -0,0 +1,84 @@
{% if error %}
<p class="err">{{ error }}</p>
{% endif %}
{% if alerts %}
<p style="font-size:.8rem;color:#888;margin:.4rem 0;">
Showing <strong>{{ total }}</strong> alert{{ '' if total == 1 else 's' }}
since <code>{{ since }}</code>
(server limit {{ limit }}{% if total == limit %} — at cap, raise limit if more expected{% endif %}).
</p>
<table>
<thead>
<tr>
<th>ID</th>
<th>Scope:Value</th>
<th>Reason</th>
<th>Country</th>
<th>AS</th>
<th>Events</th>
<th>Decisions</th>
<th>Created</th>
</tr>
</thead>
<tbody>
{% for a in alerts %}
{% set src = a.source or {} %}
<tr>
<td><code>{{ a.id }}</code></td>
<td><code>{{ src.scope or a.scope or '?' }}:{{ src.value or a.value or '?' }}</code></td>
<td>
{{ a.scenario }}
{% if a.events_count and a.events_count > 1 %}<span class="pill">×{{ a.events_count }}</span>{% endif %}
</td>
<td>{{ src.cn or '—' }}</td>
<td>{% if src.as_number %}{{ src.as_number }}{% if src.as_name %} {{ src.as_name }}{% endif %}{% else %}—{% endif %}</td>
<td>{{ a.events_count or 0 }}</td>
<td>
{% if a.decisions %}
{% for dec in a.decisions %}
<span class="pill" title="origin={{ dec.origin }} until={{ dec.until }}">{{ dec.type }} {{ dec.duration }}{% if dec.simulated %} sim{% endif %}</span>
{% endfor %}
{% else %}
{% endif %}
</td>
<td><time class="ts" datetime="{{ a.created_at }}">{{ a.created_at }}</time></td>
</tr>
{% endfor %}
</tbody>
</table>
<script>
(function(){
const RTF = new Intl.RelativeTimeFormat(undefined, { numeric: 'auto' });
const FMT = new Intl.DateTimeFormat(undefined, {
year: 'numeric', month: 'short', day: '2-digit',
hour: '2-digit', minute: '2-digit', second: '2-digit',
hour12: false
});
const UNITS = [['year',31536000],['month',2592000],['day',86400],['hour',3600],['minute',60],['second',1]];
function relative(target, now){
const diff = (target - now) / 1000;
const abs = Math.abs(diff);
for (const [u, s] of UNITS) {
if (abs >= s || u === 'second') return RTF.format(Math.round(diff / s), u);
}
}
const now = new Date();
document.querySelectorAll('#alerts time.ts').forEach(t => {
const iso = t.getAttribute('datetime');
if (!iso) return;
const d = new Date(iso);
if (isNaN(d)) return;
t.textContent = FMT.format(d);
t.title = iso + ' (' + relative(d, now) + ')';
const small = document.createElement('small');
small.style.color = '#888';
small.style.marginLeft = '.35rem';
small.textContent = relative(d, now);
t.appendChild(small);
});
})();
</script>
{% else %}
{% if not error %}<p>No alerts in window.</p>{% endif %}
{% endif %}