From b6528521cdc11198f579f0fb4964ebf04158f9dc Mon Sep 17 00:00:00 2001 From: Nazar Poshtarenko <32395926+unrenamed@users.noreply.github.com> Date: Mon, 28 Oct 2024 14:06:37 +0200 Subject: [PATCH] fix(dashboard): "No bucket found" message position on Audit Logs page (#2413) * fix(dashboard): move "bucket not found" placeholder under Audit Logs filters * fix(dashboard): filter Audit Logs by active keys and rate limits * fix(dashboard): resolve hydration error by using consistent date formatting Replace `toLocaleDateString()` with `date-fns` to ensure consistent date formatting between server and client. This prevents hydration mismatches caused by differing time zones or locale settings, ensuring stable rendering across environments. * refactor(dashboard): extract audit log mapping logic for improved readability The mapping logic within the component is complex due to nested structures. Extracting this logic into a separate function or utility enhances readability and maintainability. * [autofix.ci] apply automated fixes * fix(dashboard): silence hydration mismatch warning in audit bucket row Revert static datetime formatting using `date-fns` to `toLocaleDateString()` to better suit global users. Suppress the hydration mismatch warnings caused by differences between server and client-side rendered dates. Co-authored-by: @chronark PR: #2413 * [autofix.ci] apply automated fixes --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Andreas Thomas --- .../app/(app)/audit/[bucket]/page.tsx | 88 +++++++++++-------- .../app/(app)/audit/[bucket]/row.tsx | 6 +- internal/db/src/schema/audit_logs.ts | 3 + 3 files changed, 57 insertions(+), 40 deletions(-) diff --git a/apps/dashboard/app/(app)/audit/[bucket]/page.tsx b/apps/dashboard/app/(app)/audit/[bucket]/page.tsx index 237c2af876..e865a0c64b 100644 --- a/apps/dashboard/app/(app)/audit/[bucket]/page.tsx +++ b/apps/dashboard/app/(app)/audit/[bucket]/page.tsx @@ -7,6 +7,7 @@ import { getTenantId } from "@/lib/auth"; import { db } from "@/lib/db"; import { clerkClient } from "@clerk/nextjs"; import type { User } from "@clerk/nextjs/server"; +import type { SelectAuditLog, SelectAuditLogTarget } from "@unkey/db/src/schema"; import { unkeyAuditLogEvents } from "@unkey/schema/src/auditlog"; import { Box, X } from "lucide-react"; import Link from "next/link"; @@ -16,6 +17,7 @@ import { Suspense } from "react"; import { BucketSelect } from "./bucket-select"; import { Filter } from "./filter"; import { Row } from "./row"; + export const dynamic = "force-dynamic"; export const runtime = "edge"; @@ -31,11 +33,34 @@ type Props = { }; }; +type AuditLogWithTargets = SelectAuditLog & { targets: Array }; + /** * Parse searchParam string arrays */ const filterParser = parseAsArrayOf(parseAsString).withDefault([]); +/** + * Utility to map log with targets to log entry + */ +const toLogEntry = (l: AuditLogWithTargets) => ({ + 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, + })), +}); + export default async function AuditPage(props: Props) { const tenantId = getTenantId(); const workspace = await db.query.workspaces.findFirst({ @@ -43,6 +68,7 @@ export default async function AuditPage(props: Props) { and(eq(table.tenantId, tenantId), isNull(table.deletedAt)), with: { ratelimitNamespaces: { + where: (table, { isNull }) => isNull(table.deletedAt), columns: { id: true, name: true, @@ -87,19 +113,6 @@ export default async function AuditPage(props: Props) { }, }, }); - if (!bucket) { - return ( - - - - - Bucket Not Found - - The specified audit log bucket does not exist or you do not have access to it. - - - ); - } return (
@@ -159,29 +172,25 @@ 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} - selectedRootKeys={selectedRootKeys} - /> + {!bucket ? ( + + + + + Bucket Not Found + + The specified audit log bucket does not exist or you do not have access to it. + + + ) : ( + + )}
@@ -359,7 +368,12 @@ const UserFilter: React.FC<{ tenantId: string }> = async ({ tenantId }) => { const RootKeyFilter: React.FC<{ workspaceId: string }> = async ({ workspaceId }) => { const rootKeys = await db.query.keys.findMany({ - where: (table, { eq }) => eq(table.forWorkspaceId, workspaceId), + where: (table, { eq, and, or, isNull, gt }) => + and( + eq(table.forWorkspaceId, workspaceId), + isNull(table.deletedAt), + or(isNull(table.expires), gt(table.expires, new Date())), + ), columns: { id: true, name: true, diff --git a/apps/dashboard/app/(app)/audit/[bucket]/row.tsx b/apps/dashboard/app/(app)/audit/[bucket]/row.tsx index 2a1307ae56..a1694526a1 100644 --- a/apps/dashboard/app/(app)/audit/[bucket]/row.tsx +++ b/apps/dashboard/app/(app)/audit/[bucket]/row.tsx @@ -84,11 +84,11 @@ export const Row: React.FC = ({ auditLog, user }) => {
- + {new Date(auditLog.time).toLocaleDateString()} - - {new Date(auditLog.time).toLocaleTimeString()} + + {new Date(auditLog.time).toLocaleDateString()}
diff --git a/internal/db/src/schema/audit_logs.ts b/internal/db/src/schema/audit_logs.ts index 7b93649054..faaa9b4c6a 100644 --- a/internal/db/src/schema/audit_logs.ts +++ b/internal/db/src/schema/audit_logs.ts @@ -137,3 +137,6 @@ export const auditLogTargetRelations = relations(auditLogTarget, ({ one }) => ({ references: [auditLog.id], }), })); + +export type SelectAuditLog = typeof auditLog.$inferSelect; +export type SelectAuditLogTarget = typeof auditLogTarget.$inferSelect;