feat: alerts view + created-at timestamps on decisions
All checks were successful
Deploy / deploy (push) Successful in 22s
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:
@@ -16,7 +16,7 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<th><input type="checkbox" id="sel-all"></th>
|
||||
<th>ID</th><th>IP / value</th><th>Scope</th><th>Type</th><th>Reason</th><th>Until</th><th>Origin</th><th></th>
|
||||
<th>ID</th><th>IP / value</th><th>Scope</th><th>Type</th><th>Reason</th><th>Created</th><th>Until</th><th>Origin</th><th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -28,7 +28,8 @@
|
||||
<td>{{ d.scope }}</td>
|
||||
<td>{{ d.type }}</td>
|
||||
<td>{{ d.scenario }}</td>
|
||||
<td>{{ d.until }}</td>
|
||||
<td><time class="ts" datetime="{{ d.created_at }}">{{ d.created_at or '—' }}</time></td>
|
||||
<td><time class="ts" datetime="{{ d.until }}">{{ d.until }}</time></td>
|
||||
<td>{{ d.origin }}</td>
|
||||
<td>
|
||||
<button class="danger" type="button"
|
||||
@@ -42,6 +43,47 @@
|
||||
</table>
|
||||
</form>
|
||||
<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);
|
||||
}
|
||||
}
|
||||
function render(){
|
||||
const now = new Date();
|
||||
document.querySelectorAll('#decisions 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);
|
||||
});
|
||||
}
|
||||
render();
|
||||
setInterval(render, 30000);
|
||||
})();
|
||||
(function(){
|
||||
const form = document.getElementById('bulk-form');
|
||||
const all = document.getElementById('sel-all');
|
||||
|
||||
Reference in New Issue
Block a user