314 lines
11 KiB
Vue
314 lines
11 KiB
Vue
<template>
|
|
<template v-if="auditEntries">
|
|
<div id="audit-table">
|
|
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-600">
|
|
<thead class="bg-gray-50 dark:bg-gray-700">
|
|
<tr class="audit-table-row">
|
|
<th
|
|
scope="col"
|
|
class="audit-col-chevron p-4 text-xs font-medium tracking-wider text-left text-gray-500 uppercase dark:text-white"
|
|
>
|
|
|
|
</th>
|
|
<th
|
|
scope="col"
|
|
class="audit-col-timestamp p-4 text-xs font-medium tracking-wider text-left text-gray-500 uppercase dark:text-white"
|
|
>
|
|
Timestamp
|
|
</th>
|
|
<th
|
|
scope="col"
|
|
class="audit-col-subsystem p-4 text-xs font-medium tracking-wider text-left text-gray-500 uppercase dark:text-white"
|
|
>
|
|
Subsystem
|
|
</th>
|
|
<th
|
|
scope="col"
|
|
class="audit-col-operation p-4 text-xs font-medium tracking-wider text-left text-gray-500 uppercase dark:text-white"
|
|
>
|
|
Operation
|
|
</th>
|
|
<th
|
|
scope="col"
|
|
class="audit-col-client p-4 text-xs font-medium tracking-wider text-left text-gray-500 uppercase dark:text-white"
|
|
>
|
|
Client
|
|
</th>
|
|
<th
|
|
scope="col"
|
|
class="audit-col-secret p-4 text-xs font-medium tracking-wider text-left text-gray-500 uppercase dark:text-white"
|
|
>
|
|
Secret
|
|
</th>
|
|
<th
|
|
scope="col"
|
|
class="audit-col-message p-4 text-xs font-medium tracking-wider text-left text-gray-500 uppercase dark:text-white"
|
|
>
|
|
Message
|
|
</th>
|
|
<th
|
|
scope="col"
|
|
class="audit-col-origin p-4 text-xs font-medium tracking-wider text-left text-gray-500 uppercase dark:text-white"
|
|
>
|
|
Origin
|
|
</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody class="bg-white dark:bg-gray-800">
|
|
<template v-for="entry in auditEntries" :key="entry.id">
|
|
<tr class="audit-table-row auditRow hover:bg-gray-100 dark:hover:bg-gray-700">
|
|
<td class="audit-col-chevron">
|
|
<sl-icon-button
|
|
name="chevron-right"
|
|
@click="toggle(entry.id)"
|
|
:class="{ 'rotate-90': isExpanded(entry.id) }"
|
|
></sl-icon-button>
|
|
</td>
|
|
<td
|
|
class="audit-col-timestamp p-4 text-sm font-normal text-gray-900 whitespace-nowrap dark:text-white"
|
|
>
|
|
{{ entry.timestamp }}
|
|
</td>
|
|
<td
|
|
class="audit-col-subsystem p-4 text-sm font-normal text-gray-900 whitespace-nowrap dark:text-white"
|
|
>
|
|
{{ entry.subsystem }}
|
|
</td>
|
|
|
|
<td
|
|
class="audit-col-operation p-4 text-sm font-normal text-gray-900 whitespace-nowrap dark:text-white"
|
|
>
|
|
{{ entry.operation }}
|
|
</td>
|
|
|
|
<td
|
|
class="audit-col-client p-4 text-sm font-normal text-gray-900 whitespace-nowrap dark:text-white"
|
|
>
|
|
<abbr :title="entry.client_id" v-if="entry.client_name">{{
|
|
entry.client_name
|
|
}}</abbr>
|
|
</td>
|
|
<td
|
|
class="audit-col-secret p-4 text-sm font-normal text-gray-900 whitespace-nowrap dark:text-white"
|
|
>
|
|
<abbr :title="entry.secret_id" v-if="entry.secret_name">{{
|
|
entry.secret_name
|
|
}}</abbr>
|
|
</td>
|
|
<td
|
|
class="audit-col-message p-4 text-sm font-normal text-gray-500 dark:text-gray-400"
|
|
>
|
|
{{ entry.message }}
|
|
</td>
|
|
<td
|
|
class="audit-col-operation p-4 text-sm font-normal text-gray-500 whitespace-nowrap dark:text-gray-400"
|
|
>
|
|
{{ entry.origin }}
|
|
</td>
|
|
</tr>
|
|
<tr v-if="isExpanded(entry.id)" class="auditRow">
|
|
<td></td>
|
|
<td colspan="8">
|
|
<dl
|
|
class="max-w-md text-gray-900 divide-y divide-gray-200 dark:text-white dark:divide-gray-700 px-2 py-2"
|
|
>
|
|
<div class="flex flex-col pb-3">
|
|
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">ID</dt>
|
|
<dd class="text-xs font-semibold">{{ entry.id }}</dd>
|
|
</div>
|
|
<div class="flex flex-col pb-3">
|
|
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">Subsystem</dt>
|
|
<dd class="text-xs font-semibold">{{ entry.subsystem }}</dd>
|
|
</div>
|
|
<div class="flex flex-col pb-3">
|
|
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">Timestamp</dt>
|
|
<dd class="text-xs font-semibold">{{ entry.timestamp }}</dd>
|
|
</div>
|
|
<div class="flex flex-col pb-3">
|
|
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">Operation</dt>
|
|
<dd class="text-xs font-semibold">{{ entry.operation }}</dd>
|
|
</div>
|
|
<div class="flex flex-col pb-3">
|
|
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">Client ID</dt>
|
|
<dd class="text-xs font-semibold">{{ entry.client_id }}</dd>
|
|
</div>
|
|
<div class="flex flex-col pb-3">
|
|
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">Client Name</dt>
|
|
<dd class="text-xs font-semibold">{{ entry.client_name }}</dd>
|
|
</div>
|
|
<div class="flex flex-col pb-3">
|
|
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">Secret ID</dt>
|
|
<dd class="text-xs font-semibold">{{ entry.secret_id }}</dd>
|
|
</div>
|
|
<div class="flex flex-col pb-3">
|
|
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">Secret Name</dt>
|
|
<dd class="text-xs font-semibold">{{ entry.secret_name }}</dd>
|
|
</div>
|
|
<div class="flex flex-col pb-3">
|
|
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">Message</dt>
|
|
<dd class="text-xs font-semibold">{{ entry.message }}</dd>
|
|
</div>
|
|
<div class="flex flex-col pb-3">
|
|
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">Origin</dt>
|
|
<dd class="text-xs font-semibold">{{ entry.origin }}</dd>
|
|
</div>
|
|
<template v-if="entry.data">
|
|
<template v-for="(value, key) in entry.data">
|
|
<div class="flex flex-col pb-3">
|
|
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">{{ key }}</dt>
|
|
<dd class="text-xs font-semibold">{{ value }}</dd>
|
|
</div>
|
|
</template>
|
|
</template>
|
|
</dl>
|
|
</td>
|
|
</tr>
|
|
</template>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<template v-if="shouldPaginate">
|
|
<div
|
|
class="sticky bottom-0 right-0 items-center w-full p-4 bg-white border-t border-gray-200 sm:flex sm:justify-between dark:bg-gray-800 dark:border-gray-700"
|
|
v-if="totalPages > 0"
|
|
>
|
|
<div class="flex items-center mb-4 sm:mb-0">
|
|
<span class="text-sm font-normal text-gray-500 dark:text-gray-400">
|
|
Showing
|
|
<span
|
|
class="font-semibold text-gray-900 dark:text-white"
|
|
v-if="totalEntries < lastResult"
|
|
>
|
|
{{ firstResult }}-{{ TotalEntries }}
|
|
</span>
|
|
<span class="font-semibold text-gray-900 dark:text-white" v-else>
|
|
{{ firstResult }}-{{ lastResult }}
|
|
</span>
|
|
of
|
|
<span class="font-semibold text-gray-900 dark:text-white">
|
|
{{ totalEntries }}
|
|
</span>
|
|
</span>
|
|
</div>
|
|
<div class="flex items-center space-x-3">
|
|
<div class="flex space-x-1">
|
|
<PageNumbers
|
|
@next="nextPage"
|
|
@previous="prevPage"
|
|
@goto="goToPage"
|
|
:pageNum="pageNum"
|
|
:totalPages="totalPages"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</template>
|
|
<template v-else>
|
|
<AuditSkeleton :amount="perPage" />
|
|
</template>
|
|
</template>
|
|
<script setup lang="ts">
|
|
import { computed, ref, reactive, onMounted, watch, toRef } from 'vue'
|
|
import type { ComputedRef } from 'vue'
|
|
import { usePagination } from '@/composables/usePagination'
|
|
import { SshecretAdmin, GetAuditLogApiV1AuditGetData } from '@/client'
|
|
import type { AuditListResult } from '@/client'
|
|
import type { AuditFilter } from '@/api/types'
|
|
import { assertSdkResponseOk } from '@/api/AssertSdkResponseOk'
|
|
import PageNumbers from '@/components/common/PageNumbers.vue'
|
|
import AuditSkeleton from '@/components/audit/AuditSkeleton.vue'
|
|
|
|
interface Props {
|
|
auditFilter: AuditFilter
|
|
paginate?: boolean
|
|
}
|
|
|
|
const props = defineProps<Props>()
|
|
|
|
const shouldPaginate = toRef(() => props.paginate ?? true)
|
|
const auditFilter = toRef(() => props.auditFilter)
|
|
const auditList = ref<AuditListResult>([])
|
|
const auditEntries = computed(() => auditList.value?.results)
|
|
const totalEntries = computed(() => auditList.value?.total)
|
|
|
|
const perPage = props.auditFilter.limit ?? 25
|
|
const { pageNum, offset, firstResult, lastResult, totalPages, nextPage, prevPage, goToPage } =
|
|
usePagination(totalEntries, perPage)
|
|
|
|
const queryInput = computed<GetAuditLogApiV1AuditGetData['query']>(() => {
|
|
return {
|
|
...props.auditFilter,
|
|
offset: offset.value,
|
|
limit: perPage,
|
|
}
|
|
})
|
|
|
|
async function loadLogs() {
|
|
const response = await SshecretAdmin.getAuditLogApiV1AuditGet({
|
|
query: queryInput.value,
|
|
})
|
|
const responseData = assertSdkResponseOk(response)
|
|
auditList.value = responseData
|
|
}
|
|
|
|
const expanded = ref(new Set<string>())
|
|
|
|
function toggle(id: string) {
|
|
if (expanded.value.has(id)) {
|
|
expanded.value.delete(id)
|
|
} else {
|
|
expanded.value.add(id)
|
|
}
|
|
}
|
|
|
|
function isExpanded(id: string) {
|
|
return expanded.value.has(id)
|
|
}
|
|
|
|
watch([offset, pageNum, auditFilter], loadLogs)
|
|
onMounted(loadLogs)
|
|
</script>
|
|
<style>
|
|
@reference "tailwindcss";
|
|
tr.auditRow {
|
|
background-color: var(--color-white);
|
|
}
|
|
tr.auditRow:nth-child(even) {
|
|
background-color: var(--color-gray-50);
|
|
}
|
|
|
|
th.audit-col-chevron,
|
|
td.audit-col-chevron {
|
|
}
|
|
th.audit-col-timestamp,
|
|
td.audit-col-timestamp {
|
|
@apply flex lg:table-cell;
|
|
}
|
|
th.audit-col-subsystem,
|
|
td.audit-col-subsystem {
|
|
@apply hidden 2xl:table-cell;
|
|
}
|
|
th.audit-col-operation,
|
|
td.audit-col-operation {
|
|
@apply hidden 2xl:table-cell;
|
|
}
|
|
th.audit-col-client,
|
|
td.audit-col-client {
|
|
@apply hidden lg:table-cell;
|
|
}
|
|
th.audit-col-secret,
|
|
td.audit-col-secret {
|
|
@apply hidden lg:table-cell;
|
|
}
|
|
td.audit-col-message,
|
|
th.audit-col-message {
|
|
@apply flex lg:table-cell md:text-clip;
|
|
}
|
|
|
|
th.audit-col-origin,
|
|
td.audit-col-origin {
|
|
@apply hidden 2xl:table-cell;
|
|
}
|
|
</style>
|