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

@@ -9,12 +9,16 @@
input = $bindable(),
label,
description,
disabled = false,
type = 'text',
children,
...restProps
}: HTMLAttributes<HTMLDivElement> & {
input?: FormInput<string | boolean | number>;
label: string;
description?: string;
disabled?: boolean;
type?: 'text' | 'password' | 'email' | 'number' | 'checkbox';
children?: Snippet;
} = $props();
@@ -30,7 +34,7 @@
{#if children}
{@render children()}
{:else if input}
<Input {id} bind:value={input.value} />
<Input {id} {type} bind:value={input.value} {disabled} />
{/if}
{#if input?.error}
<p class="mt-1 text-sm text-red-500">{input.error}</p>

View File

@@ -1,6 +1,6 @@
<script lang="ts">
import { page } from '$app/stores';
import applicationConfigurationStore from '$lib/stores/application-configuration-store';
import appConfigStore from '$lib/stores/application-configuration-store';
import userStore from '$lib/stores/user-store';
import Logo from '../logo.svelte';
import HeaderAvatar from './header-avatar.svelte';
@@ -17,7 +17,7 @@
{#if !isAuthPage}
<Logo class="mr-3 h-10 w-10" />
<h1 class="text-lg font-medium" data-testid="application-name">
{$applicationConfigurationStore.appName}
{$appConfigStore.appName}
</h1>
{/if}
</div>

View File

@@ -12,7 +12,7 @@
<tr
class={cn(
"border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
"border-b transition-colors data-[state=selected]:bg-muted",
className
)}
{...$$restProps}

View File

@@ -1,29 +1,29 @@
import type {
AllApplicationConfiguration,
ApplicationConfigurationRawResponse
AllAppConfig,
AppConfigRawResponse
} from '$lib/types/application-configuration';
import APIService from './api-service';
export default class ApplicationConfigurationService extends APIService {
export default class AppConfigService extends APIService {
async list(showAll = false) {
let url = '/application-configuration';
if (showAll) {
url += '/all';
}
const { data } = await this.api.get<ApplicationConfigurationRawResponse>(url);
const { data } = await this.api.get<AppConfigRawResponse>(url);
const applicationConfiguration: Partial<AllApplicationConfiguration> = {};
const appConfig: Partial<AllAppConfig> = {};
data.forEach(({ key, value }) => {
(applicationConfiguration as any)[key] = value;
(appConfig as any)[key] = value;
});
return applicationConfiguration as AllApplicationConfiguration;
return appConfig as AllAppConfig;
}
async update(applicationConfiguration: AllApplicationConfiguration) {
const res = await this.api.put('/application-configuration', applicationConfiguration);
return res.data as AllApplicationConfiguration;
async update(appConfig: AllAppConfig) {
const res = await this.api.put('/application-configuration', appConfig);
return res.data as AllAppConfig;
}
async updateFavicon(favicon: File) {

View File

@@ -0,0 +1,20 @@
import type { AuditLog } from '$lib/types/audit-log.type';
import type { Paginated, PaginationRequest } from '$lib/types/pagination.type';
import APIService from './api-service';
class AuditLogService extends APIService {
async list(pagination?: PaginationRequest) {
const page = pagination?.page || 1;
const limit = pagination?.limit || 10;
const res = await this.api.get('/audit-logs', {
params: {
page,
limit
}
});
return res.data as Paginated<AuditLog>;
}
}
export default AuditLogService;

View File

@@ -1,22 +1,22 @@
import ApplicationConfigurationService from '$lib/services/application-configuration-service';
import type { ApplicationConfiguration } from '$lib/types/application-configuration';
import AppConfigService from '$lib/services/app-config-service';
import type { AppConfig } from '$lib/types/application-configuration';
import { writable } from 'svelte/store';
const applicationConfigurationStore = writable<ApplicationConfiguration>();
const appConfigStore = writable<AppConfig>();
const applicationConfigurationService = new ApplicationConfigurationService();
const appConfigService = new AppConfigService();
const reload = async () => {
const applicationConfiguration = await applicationConfigurationService.list();
applicationConfigurationStore.set(applicationConfiguration);
const appConfig = await appConfigService.list();
appConfigStore.set(appConfig);
};
const set = (applicationConfiguration: ApplicationConfiguration) => {
applicationConfigurationStore.set(applicationConfiguration);
}
const set = (appConfig: AppConfig) => {
appConfigStore.set(appConfig);
};
export default {
subscribe: applicationConfigurationStore.subscribe,
subscribe: appConfigStore.subscribe,
reload,
set
};

View File

@@ -1,13 +1,18 @@
export type AllApplicationConfiguration = {
export type AllAppConfig = {
appName: string;
sessionDuration: string;
sessionDuration: string;
emailEnabled: string;
smtpHost: string;
smtpPort: string;
smtpFrom: string;
smtpUser: string;
smtpPassword: string;
};
export type ApplicationConfiguration = AllApplicationConfiguration;
export type AppConfig = AllAppConfig;
export type ApplicationConfigurationRawResponse = {
export type AppConfigRawResponse = {
key: string;
type: string;
value: string;
}[];
type: string;
value: string;
}[];

View File

@@ -0,0 +1,8 @@
export type AuditLog = {
id: string;
event: string;
ipAddress: string;
device: string;
createdAt: string;
data: any;
};