1
0
mirror of https://github.com/pocket-id/pocket-id.git synced 2026-02-15 20:25:05 +00:00

feat: add audit log with email notification (#26)

This commit is contained in:
Elias Schneider
2024-09-09 10:29:41 +02:00
committed by GitHub
parent 4010ee27d6
commit 9121239dd7
51 changed files with 944 additions and 163 deletions

View File

@@ -0,0 +1,13 @@
import AuditLogService from '$lib/services/audit-log-service';
import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ cookies }) => {
const auditLogService = new AuditLogService(cookies.get('access_token'));
const auditLogs = await auditLogService.list({
limit: 15,
page: 1,
});
return {
auditLogs
};
};

View File

@@ -0,0 +1,20 @@
<script lang="ts">
import * as Card from '$lib/components/ui/card';
import AuditLogList from './audit-log-list.svelte';
let { data } = $props();
</script>
<svelte:head>
<title>Audit Log</title>
</svelte:head>
<Card.Root>
<Card.Header>
<Card.Title>Audit Log</Card.Title>
<Card.Description class="mt-1">See your account activities from the last 3 months.</Card.Description>
</Card.Header>
<Card.Content>
<AuditLogList auditLogs={data.auditLogs} />
</Card.Content>
</Card.Root>

View File

@@ -0,0 +1,95 @@
<script lang="ts">
import { Badge } from '$lib/components/ui/badge';
import * as Pagination from '$lib/components/ui/pagination';
import * as Table from '$lib/components/ui/table';
import AuditLogService from '$lib/services/audit-log-service';
import type { AuditLog } from '$lib/types/audit-log.type';
import type { Paginated, PaginationRequest } from '$lib/types/pagination.type';
let { auditLogs: initialAuditLog }: { auditLogs: Paginated<AuditLog> } = $props();
let auditLogs = $state<Paginated<AuditLog>>(initialAuditLog);
const auditLogService = new AuditLogService();
let pagination = $state<PaginationRequest>({
page: 1,
limit: 15
});
function toFriendlyEventString(event: string) {
const words = event.split('_');
const capitalizedWords = words.map((word) => {
return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
});
return capitalizedWords.join(' ');
}
</script>
<Table.Root>
<Table.Header class="whitespace-nowrap">
<Table.Row>
<Table.Head>Time</Table.Head>
<Table.Head>Event</Table.Head>
<Table.Head>IP Address</Table.Head>
<Table.Head>Device</Table.Head>
<Table.Head>Client</Table.Head>
</Table.Row>
</Table.Header>
<Table.Body class="whitespace-nowrap">
{#if auditLogs.data.length === 0}
<Table.Row>
<Table.Cell colspan={6} class="text-center">No logs found</Table.Cell>
</Table.Row>
{:else}
{#each auditLogs.data as auditLog}
<Table.Row>
<Table.Cell>{new Date(auditLog.createdAt).toLocaleString()}</Table.Cell>
<Table.Cell>
<Badge variant="outline">{toFriendlyEventString(auditLog.event)}</Badge>
</Table.Cell>
<Table.Cell>{auditLog.ipAddress}</Table.Cell>
<Table.Cell>{auditLog.device}</Table.Cell>
<Table.Cell>{auditLog.data.clientName}</Table.Cell>
</Table.Row>
{/each}
{/if}
</Table.Body>
</Table.Root>
{#if auditLogs?.data?.length ?? 0 > 0}
<Pagination.Root
class="mt-5"
count={auditLogs.pagination.totalItems}
perPage={pagination.limit}
onPageChange={async (p) =>
(auditLogs = await auditLogService.list({
page: p,
limit: pagination.limit
}))}
bind:page={auditLogs.pagination.currentPage}
let:pages
let:currentPage
>
<Pagination.Content class="flex justify-end">
<Pagination.Item>
<Pagination.PrevButton />
</Pagination.Item>
{#each pages as page (page.key)}
{#if page.type === 'ellipsis'}
<Pagination.Item>
<Pagination.Ellipsis />
</Pagination.Item>
{:else}
<Pagination.Item>
<Pagination.Link {page} isActive={auditLogs.pagination.currentPage === page.value}>
{page.value}
</Pagination.Link>
</Pagination.Item>
{/if}
{/each}
<Pagination.Item>
<Pagination.NextButton />
</Pagination.Item>
</Pagination.Content>
</Pagination.Root>
{/if}