162 lines
8.3 KiB
HTML
162 lines
8.3 KiB
HTML
{{define "title"}}Audit · restic-manager{{end}}
|
|
|
|
{{define "content"}}
|
|
{{$page := .Page}}
|
|
{{$filter := $page.Filter}}
|
|
{{$rng := $page.Range}}
|
|
<div class="max-w-[1280px] mx-auto px-8 pb-14">
|
|
|
|
{{/* crumbs */}}
|
|
<div class="crumbs pt-6">
|
|
<a href="/">Dashboard</a><span class="sep">/</span>
|
|
<span class="text-ink-mid">audit</span>
|
|
</div>
|
|
|
|
{{/* page header */}}
|
|
<div class="flex items-baseline justify-between mt-3.5">
|
|
<div>
|
|
<h1 class="text-[22px] font-medium tracking-[-0.005em]">
|
|
Audit log
|
|
<span class="text-ink-fade font-normal text-[14px] ml-2">
|
|
{{len $page.Entries}} entries · last {{if eq $rng "all"}}all-time{{else}}{{$rng}}{{end}}
|
|
</span>
|
|
</h1>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="text-ink-mute mt-2 leading-[1.55]" style="font-size: 11.5px; max-width: 760px;">
|
|
Append-only history of every operator action, agent message, and system-driven change.
|
|
Read-only — entries cannot be edited or deleted.
|
|
</div>
|
|
|
|
{{/* filter strip */}}
|
|
<div class="panel mt-4 px-4 py-3 rounded-[7px]"
|
|
style="display: grid; grid-template-columns: auto auto auto auto 1fr; gap: 14px; align-items: center;">
|
|
|
|
{{/* time-range pills */}}
|
|
<div class="inline-flex gap-1 p-[3px]" style="border: 1px solid var(--line-soft); border-radius: 5px;">
|
|
{{range list "24h" "7d" "30d" "all"}}
|
|
{{$r := .}}
|
|
{{$active := eq $r $rng}}
|
|
<a href="/audit?range={{$r}}{{if $filter.UserID}}&user_id={{$filter.UserID}}{{end}}{{if $filter.Actor}}&actor={{$filter.Actor}}{{end}}{{if $filter.ActionLike}}&action={{$filter.ActionLike}}{{end}}{{if $filter.TargetKind}}&target_kind={{$filter.TargetKind}}{{end}}"
|
|
class="btn btn-ghost"
|
|
style="padding: 5px 10px; font-size: 11.5px;{{if $active}} background: var(--panel-hi); color: var(--ink);{{end}}">
|
|
{{if eq $r "all"}}All{{else}}{{$r}}{{end}}
|
|
</a>
|
|
{{end}}
|
|
</div>
|
|
|
|
{{/* user dropdown */}}
|
|
<div>
|
|
<select class="field" style="padding: 6px 10px; font-size: 11.5px; min-width: 140px;"
|
|
onchange="window.location='/audit?range={{$rng}}&user_id='+this.value+'{{if $filter.Actor}}&actor={{$filter.Actor}}{{end}}{{if $filter.ActionLike}}&action={{$filter.ActionLike}}{{end}}{{if $filter.TargetKind}}&target_kind={{$filter.TargetKind}}{{end}}'">
|
|
<option value="" {{if eq $filter.UserID ""}}selected{{end}}>User · any</option>
|
|
{{range $id, $name := $page.UserNames}}
|
|
<option value="{{$id}}" {{if eq $filter.UserID $id}}selected{{end}}>{{$name}}</option>
|
|
{{end}}
|
|
</select>
|
|
</div>
|
|
|
|
{{/* actor dropdown — user/agent/system */}}
|
|
<div>
|
|
<select class="field" style="padding: 6px 10px; font-size: 11.5px; min-width: 130px;"
|
|
onchange="window.location='/audit?range={{$rng}}{{if $filter.UserID}}&user_id={{$filter.UserID}}{{end}}&actor='+this.value+'{{if $filter.ActionLike}}&action={{$filter.ActionLike}}{{end}}{{if $filter.TargetKind}}&target_kind={{$filter.TargetKind}}{{end}}'">
|
|
<option value="" {{if eq $filter.Actor ""}}selected{{end}}>Actor · any</option>
|
|
<option value="user" {{if eq $filter.Actor "user"}}selected{{end}}>user</option>
|
|
<option value="agent" {{if eq $filter.Actor "agent"}}selected{{end}}>agent</option>
|
|
<option value="system" {{if eq $filter.Actor "system"}}selected{{end}}>system</option>
|
|
</select>
|
|
</div>
|
|
|
|
{{/* target kind dropdown */}}
|
|
<div>
|
|
<select class="field" style="padding: 6px 10px; font-size: 11.5px; min-width: 160px;"
|
|
onchange="window.location='/audit?range={{$rng}}{{if $filter.UserID}}&user_id={{$filter.UserID}}{{end}}{{if $filter.Actor}}&actor={{$filter.Actor}}{{end}}{{if $filter.ActionLike}}&action={{$filter.ActionLike}}{{end}}&target_kind='+this.value">
|
|
<option value="" {{if eq $filter.TargetKind ""}}selected{{end}}>Target · any</option>
|
|
<option value="host" {{if eq $filter.TargetKind "host"}}selected{{end}}>host</option>
|
|
<option value="schedule" {{if eq $filter.TargetKind "schedule"}}selected{{end}}>schedule</option>
|
|
<option value="source_group" {{if eq $filter.TargetKind "source_group"}}selected{{end}}>source_group</option>
|
|
<option value="alert" {{if eq $filter.TargetKind "alert"}}selected{{end}}>alert</option>
|
|
<option value="notification_channel" {{if eq $filter.TargetKind "notification_channel"}}selected{{end}}>notification_channel</option>
|
|
<option value="job" {{if eq $filter.TargetKind "job"}}selected{{end}}>job</option>
|
|
<option value="user" {{if eq $filter.TargetKind "user"}}selected{{end}}>user</option>
|
|
</select>
|
|
</div>
|
|
|
|
{{/* action substring search */}}
|
|
<form method="get" action="/audit">
|
|
<input type="hidden" name="range" value="{{$rng}}">
|
|
{{if $filter.UserID}}<input type="hidden" name="user_id" value="{{$filter.UserID}}">{{end}}
|
|
{{if $filter.Actor}}<input type="hidden" name="actor" value="{{$filter.Actor}}">{{end}}
|
|
{{if $filter.TargetKind}}<input type="hidden" name="target_kind" value="{{$filter.TargetKind}}">{{end}}
|
|
<input type="text" name="action" value="{{$filter.ActionLike}}"
|
|
placeholder="action contains… (e.g. alert., host.)"
|
|
class="field mono"
|
|
style="padding: 6px 10px; font-size: 11.5px;">
|
|
</form>
|
|
</div>
|
|
|
|
{{/* table */}}
|
|
<div class="panel mt-3.5 rounded-[7px] overflow-hidden">
|
|
|
|
<div class="audit-row head">
|
|
<div>When</div>
|
|
<div>Actor</div>
|
|
<div>User</div>
|
|
<div>Action</div>
|
|
<div>Target</div>
|
|
<div></div>
|
|
</div>
|
|
|
|
{{if eq (len $page.Entries) 0}}
|
|
<div style="padding: 40px; text-align: center;">
|
|
<div class="text-ink text-[14px] font-medium">No matching entries.</div>
|
|
<div class="text-ink-mute text-[12px] mt-1">
|
|
{{if eq $rng "24h"}}Try widening the time range.{{else}}Adjust filters or pick a longer range.{{end}}
|
|
</div>
|
|
</div>
|
|
{{else}}
|
|
{{range $page.Entries}}
|
|
{{$e := .}}
|
|
<div class="audit-row">
|
|
<div class="mono text-[12px] text-ink-mute" title="{{absTime $e.TS}}">
|
|
{{relTime $e.TS}}
|
|
</div>
|
|
<div>
|
|
{{if eq $e.Actor "user"}}<span class="tag" style="background: color-mix(in oklch, var(--accent), transparent 92%); border-color: color-mix(in oklch, var(--accent), transparent 60%); color: var(--accent);">user</span>
|
|
{{else if eq $e.Actor "agent"}}<span class="tag" style="background: color-mix(in oklch, var(--ok), transparent 92%); border-color: color-mix(in oklch, var(--ok), transparent 60%); color: var(--ok);">agent</span>
|
|
{{else}}<span class="tag" style="background: color-mix(in oklch, var(--ink-fade), transparent 92%); color: var(--ink-mute);">system</span>{{end}}
|
|
</div>
|
|
<div class="mono text-[12px] text-ink-mid">
|
|
{{if $e.UserID}}{{$un := index $page.UserNames (deref $e.UserID)}}{{if $un}}{{$un}}{{else}}<span class="text-ink-fade">{{deref $e.UserID}}</span>{{end}}{{else}}<span class="text-ink-fade">—</span>{{end}}
|
|
</div>
|
|
<div class="mono text-[12px] text-ink">{{$e.Action}}</div>
|
|
<div class="mono text-[12px] text-ink-mute">
|
|
{{if $e.TargetKind}}
|
|
<span class="text-ink-fade">{{deref $e.TargetKind}}</span>
|
|
{{if $e.TargetID}}
|
|
{{$tid := deref $e.TargetID}}
|
|
{{if eq (deref $e.TargetKind) "host"}}{{$hn := index $page.HostNames $tid}}{{if $hn}} · {{$hn}}{{else}} · {{$tid}}{{end}}
|
|
{{else}} · {{$tid}}{{end}}
|
|
{{end}}
|
|
{{else}}
|
|
<span class="text-ink-fade">—</span>
|
|
{{end}}
|
|
</div>
|
|
<div class="text-right">
|
|
{{if and $e.Payload (gt (len $e.Payload) 2)}}
|
|
<details class="inline-block">
|
|
<summary class="text-ink-fade cursor-pointer" style="font-size: 11px;">payload</summary>
|
|
<pre class="mono text-[11px] text-ink-mute mt-2" style="white-space: pre-wrap; max-width: 400px; text-align: left; background: var(--bg); border: 1px solid var(--line-soft); border-radius: 4px; padding: 6px 8px;">{{printf "%s" $e.Payload}}</pre>
|
|
</details>
|
|
{{end}}
|
|
</div>
|
|
</div>
|
|
{{end}}
|
|
{{end}}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
{{end}}
|