← back to Lifestyle Asset Intel
views/admin_audit.ejs
82 lines
<%- include('partials/head') %>
<main class="container">
<p class="crumbs"><a href="/">← Console</a></p>
<h1>Audit — recent API calls</h1>
<p class="muted">
Reading the immutable <code>valuation_calls</code> log. Showing the last
<%= calls.length %> rows. Filter by <code>?slug=<asset-slug></code> or
<code>?limit=<1..1000></code>. JSON: <a href="/api/audit/recent">/api/audit/recent</a>.
</p>
<form method="get" action="/admin/audit" class="audit-filter">
<label>
<span class="lbl">Asset slug</span>
<input type="text" name="slug" value="<%= filterSlug || '' %>" placeholder="birkin-30-togo-gold-ghw-us">
</label>
<label>
<span class="lbl">Limit</span>
<input type="number" name="limit" value="<%= limit %>" min="1" max="1000" step="1">
</label>
<button type="submit">Apply</button>
<% if (filterSlug || limit !== 100) { %>
<a class="reset" href="/admin/audit">Reset</a>
<% } %>
</form>
<% if (!calls.length) { %>
<p class="muted">No audit rows match the current filter.</p>
<% } else { %>
<table class="audit-table">
<thead>
<tr>
<th>Called at</th>
<th>Method</th>
<th>Route</th>
<th>Slug</th>
<th class="num">Status</th>
<th class="num">Latency</th>
<th>Caller</th>
<th>Request ID</th>
</tr>
</thead>
<tbody>
<% calls.forEach(function (c) { %>
<tr>
<td class="mono"><%= fmtTs(c.called_at) %></td>
<td class="mono"><%= c.method %></td>
<td class="mono"><%= c.route %></td>
<td>
<% if (c.asset_slug) { %>
<a href="/asset/<%= c.asset_slug %>"><%= c.asset_slug %></a>
<% } else { %>
<span class="muted">—</span>
<% } %>
</td>
<td class="num <%= statusClass(c.response_status) %>"><%= c.response_status %></td>
<td class="num"><%= c.latency_ms != null ? c.latency_ms + ' ms' : '—' %></td>
<td class="mono"><%= c.caller_ip || '—' %></td>
<td class="mono small"><%= (c.request_id || '').slice(0,8) %></td>
</tr>
<% }); %>
</tbody>
</table>
<% } %>
</main>
<%
function fmtTs(v){
if(!v) return '—';
try { return new Date(v).toISOString().replace('T',' ').slice(0,19) + 'Z'; }
catch(e){ return String(v); }
}
function statusClass(s){
var n = parseInt(s,10);
if(!isFinite(n)) return '';
if(n >= 500) return 'status-5xx';
if(n >= 400) return 'status-4xx';
if(n >= 300) return 'status-3xx';
if(n >= 200) return 'status-2xx';
return '';
}
%>
<%- include('partials/foot') %>