Release 2.8.0
Publish Docker Image / build-and-push-existing-registry (./build/Dockerfile, web, pulse-signage-web) (push) Successful in 1m49s
Publish Docker Image / build-and-push-existing-registry (./build/Dockerfile.player, player, pulse-signage-player) (push) Successful in 32s

This commit is contained in:
2026-08-16 17:15:31 +01:00
parent 1bdc122995
commit 203d0bfc01
105 changed files with 4185 additions and 677 deletions
@@ -0,0 +1,57 @@
<div class="page-header d-flex flex-wrap align-items-start gap-3">
<div>
<h2>Audit log</h2>
<p>Review recorded authentication, security, and session events.</p>
</div>
</div>
<div class="card card-outline card-secondary" data-table-search-container data-table-pagination-card>
<div class="card-header">
<form method="get" action="/settings/audit-log" class="row g-2 align-items-end">
<div class="col-12 col-md-3">
<label class="form-label small" for="audit-category">Category</label>
<select id="audit-category" name="category" class="form-select form-select-sm">
<option value="">All categories</option>
{{#each categories}}<option value="{{this}}" {{#if (eq ../selectedCategory this)}}selected{{/if}}>{{this}}</option>{{/each}}
</select>
</div>
<div class="col-12 col-md-3">
<label class="form-label small" for="audit-event-type">Event type</label>
<input id="audit-event-type" name="event_type" class="form-control form-control-sm" value="{{selectedEventType}}" placeholder="e.g. login.success" list="audit-event-type-options" autocomplete="off">
<datalist id="audit-event-type-options">
{{#each eventTypes}}<option value="{{this}}"></option>{{/each}}
</datalist>
</div>
<div class="col-12 col-md-4">
<label class="form-label small" for="audit-search">Search</label>
<input id="audit-search" name="search" type="search" class="form-control form-control-sm" value="{{search}}" placeholder="Event, actor, or target" data-table-search>
</div>
<div class="col-12 col-md-2 d-flex gap-2">
<button type="submit" class="btn btn-primary btn-sm flex-fill"><i class="bi bi-search me-1" aria-hidden="true"></i>Filter</button>
{{#if (hasPermission currentUser 'audit-log.allow')}}<a class="btn btn-outline-primary btn-sm flex-fill" href="{{exportUrl}}"><i class="bi bi-download me-1" aria-hidden="true"></i>Export</a>{{/if}}
</div>
</form>
</div>
<div class="card-body p-0 table-responsive">
<table class="table table-striped align-middle mb-0" data-table-searchable>
<thead><tr><th data-table-sort-key="time">Time</th><th data-table-sort-key="event">Event</th><th data-table-sort-key="actor">Actor</th><th data-table-sort-key="target">Target</th><th>Source</th><th>Details</th></tr></thead>
<tbody>
{{#if events.length}}
{{#each events}}
<tr data-table-search-row>
<td class="text-nowrap"><time data-local-datetime data-local-datetime-format="24h" datetime="{{occurredAtValue}}">{{occurredAtValue}}</time></td>
<td><div class="fw-semibold text-capitalize">{{eventLabel}}</div><div class="text-muted small">{{category}}</div></td>
<td>{{actorLabel}}</td>
<td>{{targetLabelDisplay}}</td>
<td><div>{{ip_address}}</div><div class="text-muted small text-break">{{user_agent}}</div></td>
<td class="text-break"><small>{{detailsDisplay}}</small></td>
</tr>
{{/each}}
{{else}}
<tr data-table-search-empty-default><td colspan="6" class="empty">No audit events match these filters.</td></tr>
{{/if}}
</tbody>
</table>
</div>
{{> table-pagination pagination=pagination basePath="/settings/audit-log" alwaysShow=true}}
</div>