Lumi/src/web/views/admin-logs.ejs
2026-06-17 05:11:30 +02:00

133 lines
6.2 KiB
Plaintext

<%- include("partials/layout-top", { title }) %>
<% const filters = logFilters || { range: '86400000', level: 'all', limit: '50' }; %>
<section class="card">
<div class="section-header">
<div>
<h1>Logs</h1>
<p class="command-subtitle">Core system logs with severity, timestamps, and details.</p>
</div>
<div class="log-controls">
<label>
<span>Search</span>
<input
class="table-search"
type="search"
placeholder="Search logs"
aria-label="Search logs"
data-log-search
/>
</label>
<label>
<span>Severity</span>
<select class="table-search" data-log-level aria-label="Filter log severity">
<option value="all" <%= filters.level === 'all' ? 'selected' : '' %>>All severities</option>
<option value="error" <%= filters.level === 'error' ? 'selected' : '' %>>Error</option>
<option value="warn" <%= filters.level === 'warn' ? 'selected' : '' %>>Warning</option>
<option value="info" <%= filters.level === 'info' ? 'selected' : '' %>>Info</option>
<option value="debug" <%= filters.level === 'debug' ? 'selected' : '' %>>Debug</option>
</select>
</label>
<label>
<span>Range</span>
<select class="table-search" data-log-range aria-label="Filter by time range">
<option value="all" <%= filters.range === 'all' ? 'selected' : '' %>>All time</option>
<option value="<%= 5 * 60 * 1000 %>" <%= filters.range === `${5 * 60 * 1000}` ? 'selected' : '' %>>Last 5 minutes</option>
<option value="<%= 60 * 60 * 1000 %>" <%= filters.range === `${60 * 60 * 1000}` ? 'selected' : '' %>>Last hour</option>
<option value="<%= 24 * 60 * 60 * 1000 %>" <%= filters.range === `${24 * 60 * 60 * 1000}` ? 'selected' : '' %>>Last 24 hours</option>
<option value="<%= 7 * 24 * 60 * 60 * 1000 %>" <%= filters.range === `${7 * 24 * 60 * 60 * 1000}` ? 'selected' : '' %>>Last week</option>
<option value="<%= 30 * 24 * 60 * 60 * 1000 %>" <%= filters.range === `${30 * 24 * 60 * 60 * 1000}` ? 'selected' : '' %>>Last month</option>
</select>
</label>
<label>
<span>Entries</span>
<select class="table-search" data-log-limit aria-label="Limit log entries">
<option value="50" <%= filters.limit === '50' ? 'selected' : '' %>>50 most recent</option>
<option value="100" <%= filters.limit === '100' ? 'selected' : '' %>>100 most recent</option>
<option value="250" <%= filters.limit === '250' ? 'selected' : '' %>>250 most recent</option>
<option value="500" <%= filters.limit === '500' ? 'selected' : '' %>>500 most recent</option>
</select>
</label>
<a class="button subtle" href="/admin/logs">Reset</a>
<a class="button subtle" href="<%= `/admin/logs?range=${encodeURIComponent(filters.range)}&level=${encodeURIComponent(filters.level)}&limit=${encodeURIComponent(filters.limit)}` %>">Refresh</a>
<button type="button" class="button subtle" data-log-download>Download logs</button>
</div>
</div>
<div class="log-window" data-log-list>
<% if (!logs || !logs.length) { %>
<p class="hint">No log events yet.</p>
<% } else { %>
<% logs.forEach((log) => { %>
<details
class="log-entry level-<%= log.level %>"
data-log-entry
data-level="<%= log.level %>"
data-timestamp="<%= log.created_at %>"
data-search="<%= `${log.message} ${log.details || ""}`.toLowerCase() %>"
>
<summary>
<span class="log-marker" aria-hidden="true"></span>
<span class="log-message"><%= log.message %></span>
<span class="log-level-pill"><%= log.level %></span>
<span class="log-time"><%= new Date(log.created_at).toLocaleString() %></span>
</summary>
<% if (log.details) { %>
<pre class="log-details"><%= log.details %></pre>
<% } else { %>
<div class="log-details empty">No additional details.</div>
<% } %>
</details>
<% }) %>
<% } %>
</div>
</section>
<div class="modal-backdrop" data-log-modal aria-hidden="true">
<div class="modal">
<div class="modal-header">
<h3>Download logs</h3>
<button type="button" class="icon-button" data-modal-close aria-label="Close">
<svg viewBox="0 0 24 24" aria-hidden="true">
<path d="M6 6l12 12M18 6l-12 12" stroke="currentColor" stroke-width="2" stroke-linecap="round" />
</svg>
</button>
</div>
<form method="get" action="/admin/logs/download" class="form-grid">
<div class="field">
<label>Timespan</label>
<select name="range">
<option value="all">All time</option>
<option value="<%= 5 * 60 * 1000 %>">Last 5 minutes</option>
<option value="<%= 60 * 60 * 1000 %>">Last hour</option>
<option value="<%= 24 * 60 * 60 * 1000 %>">Last 24 hours</option>
<option value="<%= 7 * 24 * 60 * 60 * 1000 %>">Last week</option>
<option value="<%= 30 * 24 * 60 * 60 * 1000 %>">Last month</option>
</select>
</div>
<div class="field full">
<label>Severities</label>
<div class="checkbox-grid">
<label><input type="checkbox" name="level" value="error" /> Error</label>
<label><input type="checkbox" name="level" value="warn" /> Warning</label>
<label><input type="checkbox" name="level" value="info" /> Info</label>
<label><input type="checkbox" name="level" value="debug" /> Debug</label>
</div>
<p class="hint">Leave unchecked for all severities.</p>
</div>
<div class="field">
<label>Entries</label>
<select name="limit">
<option value="50">50 most recent</option>
<option value="100">100 most recent</option>
<option value="250">250 most recent</option>
<option value="500">500 most recent</option>
<option value="all">All entries</option>
</select>
</div>
<div class="modal-actions">
<button type="button" class="button subtle" data-modal-close>Cancel</button>
<button type="submit" class="button">Download</button>
</div>
</form>
</div>
</div>
<%- include("partials/layout-bottom") %>