diff --git a/src/components/moderator/App.svelte b/src/components/moderator/App.svelte
index cb0dc13..0d12441 100644
--- a/src/components/moderator/App.svelte
+++ b/src/components/moderator/App.svelte
@@ -27,6 +27,7 @@
import Event from "@components/moderator/pages/event/Event.svelte";
import Pages from "@components/moderator/pages/pages/Pages.svelte";
import Generator from "@components/moderator/pages/generators/Generator.svelte";
+ import AuditLog from "@components/moderator/pages/logs/AuditLog.svelte";
import { Tooltip } from "bits-ui";
const routes: RouteDefinition = {
@@ -36,6 +37,7 @@
"/event/:id": Event,
"/event/:id/generate": Generator,
"/pages": Pages,
+ "/logs": AuditLog,
};
diff --git a/src/components/moderator/layout/NavLinks.svelte b/src/components/moderator/layout/NavLinks.svelte
index 1b540e7..f058c3d 100644
--- a/src/components/moderator/layout/NavLinks.svelte
+++ b/src/components/moderator/layout/NavLinks.svelte
@@ -27,4 +27,5 @@
Players
Pages
Schematics
+ Logs
diff --git a/src/components/moderator/pages/logs/AuditLog.svelte b/src/components/moderator/pages/logs/AuditLog.svelte
new file mode 100644
index 0000000..b436f0a
--- /dev/null
+++ b/src/components/moderator/pages/logs/AuditLog.svelte
@@ -0,0 +1,235 @@
+
+
+
+
+
+ debounce(e.currentTarget.value, (v) => {
+ fullText = v;
+ })}
+ oninput={(e) =>
+ debounce(e.currentTarget.value, (v) => {
+ fullText = v;
+ })}
+ />
+
+
+
+
+
+
+
+
+
+
+ No Players found :(
+
+ {#each $players.filter((v) => v.name.toLowerCase().includes(playerSearch.toLowerCase())).filter((v, i) => i < 50) as player (player.uuid)}
+ (actors = actors.includes(player.uuid) ? actors.filter((v) => v !== player.uuid) : [...actors, player.uuid])}
+ keywords={[player.uuid]}
+ >
+
+ {player.name}
+
+ {/each}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ No Players found :(
+
+ {#each $players.filter((v) => v.name.toLowerCase().includes(ownerSearch.toLowerCase())).filter((v, i) => i < 50) as player (player.uuid)}
+ (serverOwner = serverOwner.includes(player.uuid) ? serverOwner.filter((v) => v !== player.uuid) : [...serverOwner, player.uuid])}
+ keywords={[player.uuid]}
+ >
+
+ {player.name}
+
+ {/each}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {#each table.getHeaderGroups() as headerGroup (headerGroup.id)}
+
+ {#each headerGroup.headers as header (header.id)}
+
+ {#if !header.isPlaceholder}
+
+ {/if}
+
+ {/each}
+
+ {/each}
+
+
+ {#each table.getRowModel().rows as row (row.id)}
+
+ {#each row.getVisibleCells() as cell (cell.id)}
+
+
+
+ {/each}
+
+ {:else}
+
+ Keine Einträge gefunden.
+
+ {/each}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/moderator/pages/logs/columns.ts b/src/components/moderator/pages/logs/columns.ts
new file mode 100644
index 0000000..805cb9e
--- /dev/null
+++ b/src/components/moderator/pages/logs/columns.ts
@@ -0,0 +1,35 @@
+import type { AuditLogEntry } from "@components/types/auditlog";
+import type { ColumnDef } from "@tanstack/table-core";
+
+export const columns: ColumnDef[] = [
+ {
+ accessorKey: "id",
+ header: "ID",
+ },
+ {
+ accessorKey: "time",
+ header: "Time",
+ cell: (info) => new Date(info.getValue()).toLocaleString(),
+ },
+ {
+ accessorKey: "server",
+ header: "Server",
+ },
+ {
+ accessorKey: "serverOwner",
+ header: "Server Owner",
+ cell: (info) => info.getValue() || "N/A",
+ },
+ {
+ accessorKey: "actor",
+ header: "Spieler",
+ },
+ {
+ accessorKey: "actionType",
+ header: "Action Type",
+ },
+ {
+ accessorKey: "actionText",
+ header: "Action Text",
+ },
+];
diff --git a/src/components/repo/auditlog.ts b/src/components/repo/auditlog.ts
new file mode 100644
index 0000000..ee2609b
--- /dev/null
+++ b/src/components/repo/auditlog.ts
@@ -0,0 +1,40 @@
+import { derived } from "svelte/store";
+import { fetchWithToken, tokenStore } from "./repo";
+import { PagedAutidLogSchema } from "@components/types/auditlog";
+
+export class AuditLogRepo {
+ async get(
+ actionText: string | undefined,
+ serverText: string | undefined,
+ fullText: string | undefined,
+ actor: string[] | undefined,
+ actionType: string[] | undefined,
+ timeFrom: number | undefined,
+ timeTo: number | undefined,
+ serverOwner: string[] | undefined,
+ velocity: boolean | undefined,
+ page: number,
+ pageSize: number,
+ sorting: string | undefined
+ ) {
+ const params = new URLSearchParams();
+ if (actionText) params.append("actionText", actionText);
+ if (serverText) params.append("serverText", serverText);
+ if (fullText) params.append("fullText", fullText);
+ if (actor) actor.forEach((a) => params.append("actor", a.toString()));
+ if (actionType) actionType.forEach((a) => params.append("actionType", a));
+ if (timeFrom) params.append("timeGreater", timeFrom.toString());
+ if (timeTo) params.append("timeLess", timeTo.toString());
+ if (serverOwner) serverOwner.forEach((s) => params.append("serverOwner", s.toString()));
+ if (velocity !== undefined) params.append("velocity", velocity.toString());
+ params.append("page", page.toString());
+ params.append("limit", pageSize.toString());
+ if (sorting) params.append("sorting", sorting);
+
+ return await fetchWithToken("", `/auditlog?${params.toString()}`)
+ .then((value) => value.json())
+ .then((data) => PagedAutidLogSchema.parse(data));
+ }
+}
+
+export const auditLog = derived(tokenStore, ($token) => new AuditLogRepo());
diff --git a/src/components/types/auditlog.ts b/src/components/types/auditlog.ts
new file mode 100644
index 0000000..d956980
--- /dev/null
+++ b/src/components/types/auditlog.ts
@@ -0,0 +1,19 @@
+import { z } from "zod";
+
+export const AuditLogEntrySchema = z.object({
+ id: z.number(),
+ time: z.number(),
+ server: z.string(),
+ serverOwner: z.string().nullable(),
+ actor: z.string(),
+ actionType: z.enum(["JOIN", "LEAVE", "COMMAND", "SENSITIVE_COMMAND", "CHAT", "GUI_OPEN", "GUI_CLOSE", "GUI_CLICK"]),
+ actionText: z.string(),
+});
+
+export const PagedAutidLogSchema = z.object({
+ entries: z.array(AuditLogEntrySchema),
+ rows: z.number(),
+});
+
+export type AuditLogEntry = z.infer;
+export type PagedAuditLog = z.infer;