From 0d7e04a03b8cf17b6a904d8d9e4cf177852d5b5c Mon Sep 17 00:00:00 2001 From: chronark Date: Fri, 4 Oct 2024 18:40:55 +0200 Subject: [PATCH 1/7] refactor: query audit logs from planetscale --- .../app/(app)/audit/[bucket]/page.tsx | 102 ++++++++++++------ .../app/(app)/audit/[bucket]/row.tsx | 9 +- internal/db/src/schema/audit_logs.ts | 8 ++ 3 files changed, 83 insertions(+), 36 deletions(-) diff --git a/apps/dashboard/app/(app)/audit/[bucket]/page.tsx b/apps/dashboard/app/(app)/audit/[bucket]/page.tsx index 90474c3d43..3e7e9c7b59 100644 --- a/apps/dashboard/app/(app)/audit/[bucket]/page.tsx +++ b/apps/dashboard/app/(app)/audit/[bucket]/page.tsx @@ -9,19 +9,16 @@ import { EmptyPlaceholder } from "@/components/dashboard/empty-placeholder"; import { Loading } from "@/components/dashboard/loading"; import { Button } from "@/components/ui/button"; import { Table, TableBody, TableHead, TableHeader, TableRow } from "@/components/ui/table"; -import { type auditLogsDataSchema, getAuditLogs } from "@/lib/tinybird"; import { unkeyAuditLogEvents } from "@unkey/schema/src/auditlog"; import { Box, X } from "lucide-react"; import Link from "next/link"; import { parseAsArrayOf, parseAsString } from "nuqs/server"; import { Suspense } from "react"; -import type { z } from "zod"; import { BucketSelect } from "./bucket-select"; import { Filter } from "./filter"; import { Row } from "./row"; export const dynamic = "force-dynamic"; export const runtime = "edge"; -import { ExportCsv } from "./export-csv"; type Props = { params: { @@ -67,21 +64,36 @@ export default async function AuditPage(props: Props) { */ const retentionDays = workspace.features.auditLogRetentionDays ?? workspace.plan === "free" ? 30 : 90; + const retentionCutoffUnixMilli = Date.now() - retentionDays * 24 * 60 * 60 * 1000; - const logs = await getAuditLogs({ - workspaceId: workspace.id, - before: props.searchParams.before ? Number(props.searchParams.before) : undefined, - after: Date.now() - retentionDays * 24 * 60 * 60 * 1000, - bucket: props.params.bucket, - events: selectedEvents.length > 0 ? selectedEvents : undefined, - actorIds: - selectedUsers.length > 0 || selectedRootKeys.length > 0 - ? [...selectedUsers, ...selectedRootKeys] - : undefined, - }).catch((err) => { - console.error(err); - throw err; + const selectedActorIds = [...selectedRootKeys, ...selectedUsers]; + + const bucket = await db.query.auditLogBucket.findFirst({ + where: (table, { eq, and }) => + and(eq(table.workspaceId, workspace.id), eq(table.name, props.params.bucket)), + with: { + logs: { + where: (table, { and, inArray, gte }) => + and( + selectedEvents.length > 0 ? inArray(table.event, selectedEvents) : undefined, + gte(table.createdAt, retentionCutoffUnixMilli), + selectedActorIds.length > 0 ? inArray(table.actorId, selectedActorIds) : undefined, + ), + with: { + targets: true, + }, + }, + }, }); + if (!bucket) { + return ( + + + + + + ); + } return (
@@ -118,7 +130,7 @@ export default async function AuditPage(props: Props) { ) : null} }> - + {/**/} {selectedEvents.length > 0 || selectedUsers.length > 0 || selectedRootKeys.length > 0 ? ( @@ -143,7 +155,23 @@ export default async function AuditPage(props: Props) { } > ({ + id: l.id, + event: l.event, + time: l.time, + actor: { + id: l.actorId, + name: l.actorName, + type: l.actorType, + }, + location: l.remoteIp, + description: l.display, + targets: l.targets.map((t) => ({ + id: t.id, + type: t.type, + name: t.name, + })), + }))} before={props.searchParams.before ? Number(props.searchParams.before) : undefined} selectedEvents={selectedEvents} selectedUsers={selectedUsers} @@ -160,12 +188,28 @@ const AuditLogTable: React.FC<{ selectedUsers: string[]; selectedRootKeys: string[]; before?: number; - logs: { data: z.infer[] }; + logs: Array<{ + id: string; + event: string; + time: number; + actor: { + id: string; + type: string; + name: string | null; + }; + location: string | null; + description: string; + targets: Array<{ + id: string; + type: string; + name: string | null; + }>; + }>; }> = async ({ selectedEvents, selectedRootKeys, selectedUsers, before, logs }) => { const isFiltered = selectedEvents.length > 0 || selectedUsers.length > 0 || selectedRootKeys.length > 0 || before; - if (logs.data.length === 0) { + if (logs.length === 0) { return ( @@ -190,7 +234,7 @@ const AuditLogTable: React.FC<{ ); } - const hasMoreLogs = logs.data.length >= 100; + const hasMoreLogs = logs.length >= 100; function buildHref(override: Partial): string { const searchParams = new URLSearchParams(); @@ -213,9 +257,7 @@ const AuditLogTable: React.FC<{ return `/audit?${searchParams.toString()}`; } - const userIds = [ - ...new Set(logs.data.filter((l) => l.actor.type === "user").map((l) => l.actor.id)), - ]; + const userIds = [...new Set(logs.filter((l) => l.actor.type === "user").map((l) => l.actor.id))]; const users = ( await Promise.all(userIds.map((userId) => clerkClient.users.getUser(userId).catch(() => null))) ).reduce( @@ -234,18 +276,16 @@ const AuditLogTable: React.FC<{ Actor - Event - Location Time - {logs.data.map((l) => { + {logs.map((l) => { const user = users[l.actor.id]; return ( @@ -271,7 +311,7 @@ const AuditLogTable: React.FC<{
- + - ); -} diff --git a/apps/dashboard/app/(app)/audit/[bucket]/page.tsx b/apps/dashboard/app/(app)/audit/[bucket]/page.tsx index 85f1e8f67a..93a019b09e 100644 --- a/apps/dashboard/app/(app)/audit/[bucket]/page.tsx +++ b/apps/dashboard/app/(app)/audit/[bucket]/page.tsx @@ -1,17 +1,16 @@ +import { EmptyPlaceholder } from "@/components/dashboard/empty-placeholder"; +import { Loading } from "@/components/dashboard/loading"; import { PageHeader } from "@/components/dashboard/page-header"; +import { Button } from "@/components/ui/button"; +import { Table, TableBody, TableHead, TableHeader, TableRow } from "@/components/ui/table"; import { getTenantId } from "@/lib/auth"; import { db } from "@/lib/db"; import { clerkClient } from "@clerk/nextjs"; import type { User } from "@clerk/nextjs/server"; -import { redirect } from "next/navigation"; - -import { EmptyPlaceholder } from "@/components/dashboard/empty-placeholder"; -import { Loading } from "@/components/dashboard/loading"; -import { Button } from "@/components/ui/button"; -import { Table, TableBody, TableHead, TableHeader, TableRow } from "@/components/ui/table"; import { unkeyAuditLogEvents } from "@unkey/schema/src/auditlog"; import { Box, X } from "lucide-react"; import Link from "next/link"; +import { redirect } from "next/navigation"; import { parseAsArrayOf, parseAsString } from "nuqs/server"; import { Suspense } from "react"; import { BucketSelect } from "./bucket-select"; @@ -83,7 +82,8 @@ export default async function AuditPage(props: Props) { with: { targets: true, }, - orderBy: (table, { asc }) => asc(table.id) + orderBy: (table, { asc }) => asc(table.id), + limit: 100 }, }, }); @@ -132,7 +132,6 @@ export default async function AuditPage(props: Props) { ) : null} }> - {/**/} {selectedEvents.length > 0 || selectedUsers.length > 0 || selectedRootKeys.length > 0 ? ( From c74a0658e786142285ca75cd72173e605c8c8241 Mon Sep 17 00:00:00 2001 From: Andreas Thomas Date: Fri, 4 Oct 2024 19:02:17 +0200 Subject: [PATCH 5/7] Update apps/dashboard/app/(app)/audit/[bucket]/page.tsx Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- apps/dashboard/app/(app)/audit/[bucket]/page.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/dashboard/app/(app)/audit/[bucket]/page.tsx b/apps/dashboard/app/(app)/audit/[bucket]/page.tsx index a274a804b0..69c4797e25 100644 --- a/apps/dashboard/app/(app)/audit/[bucket]/page.tsx +++ b/apps/dashboard/app/(app)/audit/[bucket]/page.tsx @@ -91,8 +91,12 @@ export default async function AuditPage(props: Props) { return ( - + + Bucket Not Found + + The specified audit log bucket does not exist or you do not have access to it. + ); } From 8778acec0f775b8f3db8c6fbcc324a1d45e94ac8 Mon Sep 17 00:00:00 2001 From: chronark Date: Fri, 4 Oct 2024 19:06:18 +0200 Subject: [PATCH 6/7] fmt: add comma --- apps/dashboard/app/(app)/audit/[bucket]/page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dashboard/app/(app)/audit/[bucket]/page.tsx b/apps/dashboard/app/(app)/audit/[bucket]/page.tsx index 46396374fd..b5c746ecbe 100644 --- a/apps/dashboard/app/(app)/audit/[bucket]/page.tsx +++ b/apps/dashboard/app/(app)/audit/[bucket]/page.tsx @@ -83,7 +83,7 @@ export default async function AuditPage(props: Props) { targets: true, }, orderBy: (table, { asc }) => asc(table.id), - limit: 100 + limit: 100, }, }, }); From 9a4e209f07b9de0d415a30b4eac734046d09d918 Mon Sep 17 00:00:00 2001 From: chronark Date: Fri, 4 Oct 2024 19:10:43 +0200 Subject: [PATCH 7/7] ci: remove wrong lint command --- .github/workflows/autofix.ci.yaml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/autofix.ci.yaml b/.github/workflows/autofix.ci.yaml index 82df563d81..510498089e 100644 --- a/.github/workflows/autofix.ci.yaml +++ b/.github/workflows/autofix.ci.yaml @@ -35,6 +35,3 @@ jobs: run: npx mintlify@latest broken-links working-directory: apps/docs - - name: Lint engineering docs - run: npx mintlify@latest broken-links - working-directory: apps/engineering