117 lines
4.1 KiB
Vue

<script setup lang="ts">
import { useNuxtApp, useRoute, useFetch } from 'nuxt/app';
import useSimpleHead from '~/composables/useSimpleHead.ts';
const { $translator: translator } = useNuxtApp();
useSimpleHead({
title: `${translator.translate('admin.header')} • Audit log`,
}, translator);
const route = useRoute();
const { data: logEntries } = await useFetch<any[]>(
`/api/admin/audit-log/${route.params.username}/${route.params.id}`,
{ default: () => [] },
);
const username = route.params.username;
const userId = route.params.id;
const categoryFilter = ref<string | null>(null);
const visibleLogEntries = computed(() => {
return logEntries.value.filter((logEntry) => {
return categoryFilter.value === null || logEntry.event.startsWith(`${categoryFilter.value}/`);
});
});
const categories = computed(() => {
return new Set(logEntries.value.map((e) => e.event.split('/')[0]));
});
</script>
<template>
<Page wide>
<NotFound v-if="!$isGranted('*')" />
<div v-else>
<p>
<nuxt-link to="/admin">
<Icon v="user-cog" />
<T>admin.header</T>
</nuxt-link>
</p>
<h2>
<Icon v="file-search" />
Audit log
<br>
<small class="text-muted">
(username: {{ username }}, id: {{ userId }})
</small>
</h2>
<div v-if="categories.size > 0" class="btn-group">
<button
v-for="category in categories"
type="button"
:class="['btn', category === categoryFilter ? 'btn-primary' : 'btn-outline-primary']"
@click="categoryFilter = categoryFilter === category ? null : category"
>
{{ category }}
</button>
</div>
<div class="table-responsive mt-4">
<table class="table">
<thead>
<tr>
<th>Event time</th>
<th>Username</th>
<th>User ID</th>
<th>Event</th>
<th>Payload</th>
</tr>
</thead>
<tbody>
<tr v-for="logEntry in visibleLogEntries">
<th>
{{ $datetime($ulidTime(logEntry.id)) }}
</th>
<td>
{{ logEntry.username }}
<template v-if="logEntry.username !== username">
<br>
<span class="badge bg-warning">Username mismatch</span>
</template>
</td>
<td>
{{ logEntry.userId }}
<template v-if="logEntry.aboutUserId !== null">
<br>
about: {{ logEntry.aboutUserId }}
</template>
<template v-else-if="logEntry.userId !== userId">
<br>
<span class="badge bg-warning">ID mismatch</span>
</template>
</td>
<td>
<strong>{{ logEntry.event.split('/')[0] }}</strong>/{{ logEntry.event.split('/')[1] }}
</td>
<td>
<pre v-if="logEntry.payload">{{ JSON.stringify(logEntry.payload, null, 4) }}</pre>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</Page>
</template>
<style lang="scss" scoped>
pre {
max-height: 300px;
max-width: 400px;
overflow: auto;
}
</style>