mirror of
https://gitlab.com/PronounsPage/PronounsPage.git
synced 2025-09-25 22:19:28 -04:00
117 lines
4.1 KiB
Vue
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>
|