diff --git a/apps/dashboard/app/(app)/apis/[apiId]/_overview/components/table/components/override-indicator.tsx b/apps/dashboard/app/(app)/apis/[apiId]/_overview/components/table/components/override-indicator.tsx index e5419c9f66..4afcf97a09 100644 --- a/apps/dashboard/app/(app)/apis/[apiId]/_overview/components/table/components/override-indicator.tsx +++ b/apps/dashboard/app/(app)/apis/[apiId]/_overview/components/table/components/override-indicator.tsx @@ -1,4 +1,5 @@ "use client"; +import { shortenId } from "@/lib/shorten-id"; import { cn } from "@/lib/utils"; import type { KeysOverviewLog } from "@unkey/clickhouse/src/keys/keys"; import { TriangleWarning2 } from "@unkey/icons"; @@ -86,8 +87,7 @@ export const KeyIdentifierColumn = ({ log, apiId, onNavigate }: KeyIdentifierCol onClick={handleLinkClick} >
- {log.key_id.substring(0, 8)}... - {log.key_id.substring(log.key_id.length - 4)} + {shortenId(log.key_id)}
diff --git a/apps/dashboard/app/(app)/apis/[apiId]/api-id-navbar.tsx b/apps/dashboard/app/(app)/apis/[apiId]/api-id-navbar.tsx index f37a7d10a6..6c85af5a42 100644 --- a/apps/dashboard/app/(app)/apis/[apiId]/api-id-navbar.tsx +++ b/apps/dashboard/app/(app)/apis/[apiId]/api-id-navbar.tsx @@ -5,6 +5,7 @@ import { NavbarActionButton } from "@/components/navigation/action-button"; import { CopyableIDButton } from "@/components/navigation/copyable-id-button"; import { Navbar } from "@/components/navigation/navbar"; import { useIsMobile } from "@/hooks/use-mobile"; +import { shortenId } from "@/lib/shorten-id"; import { trpc } from "@/lib/trpc/client"; import { ChevronExpandY, Gear, Nodes, Plus, TaskUnchecked } from "@unkey/icons"; import dynamic from "next/dynamic"; @@ -190,8 +191,7 @@ export const ApisNavbar = ({ isIdentifier active > - {specificKey.id?.substring(0, 8)}... - {specificKey.id?.substring(specificKey.id?.length - 4)} + {shortenId(specificKey.id)} )} diff --git a/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/[keyId]/components/table/logs-table.tsx b/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/[keyId]/components/table/logs-table.tsx index 9d9f2f072b..a8f8a7672b 100644 --- a/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/[keyId]/components/table/logs-table.tsx +++ b/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/[keyId]/components/table/logs-table.tsx @@ -1,6 +1,7 @@ "use client"; import { VirtualTable } from "@/components/virtual-table/index"; import type { Column } from "@/components/virtual-table/types"; +import { shortenId } from "@/lib/shorten-id"; import { trpc } from "@/lib/trpc/client"; import { cn } from "@/lib/utils"; import { useQueryTime } from "@/providers/query-time-provider"; @@ -311,6 +312,7 @@ export const KeyDetailsLogsTable = ({ keyspaceId, keyId, selectedLog, onLogSelec log.tags.slice(0, 3).map((tag) => ( @@ -329,7 +331,7 @@ export const KeyDetailsLogsTable = ({ keyspaceId, keyId, selectedLog, onLogSelec ) : ( -
+
{tag}
{/* biome-ignore lint/a11y/useKeyWithClickEvents: */}
- {tag.length > 15 ? `${tag.substring(0, 12)}...` : tag} + {shortenId(tag, { + endChars: 0, + minLength: 14, + startChars: 10, + })} )) diff --git a/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/_components/components/table/keys-list.tsx b/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/_components/components/table/keys-list.tsx index 980f410785..5bdfa1f9ed 100644 --- a/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/_components/components/table/keys-list.tsx +++ b/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/_components/components/table/keys-list.tsx @@ -1,6 +1,7 @@ "use client"; import { VirtualTable } from "@/components/virtual-table/index"; import type { Column } from "@/components/virtual-table/types"; +import { shortenId } from "@/lib/shorten-id"; import type { KeyDetails } from "@/lib/trpc/routers/api/keys/query-api-keys/schema"; import { BookBookmark, Dots, Focus, Key } from "@unkey/icons"; import { Button, Checkbox, Empty, InfoTooltip, Loading } from "@unkey/ui"; @@ -182,8 +183,7 @@ export const KeysList = ({ }} >
- {key.id.substring(0, 8)}... - {key.id.substring(key.id.length - 4)} + {shortenId(key.id)}
{key.name && ( diff --git a/apps/dashboard/app/(app)/audit/components/controls/components/logs-filters/components/root-keys-filter.tsx b/apps/dashboard/app/(app)/audit/components/controls/components/logs-filters/components/root-keys-filter.tsx index 575673aa4c..67cb6b8bdd 100644 --- a/apps/dashboard/app/(app)/audit/components/controls/components/logs-filters/components/root-keys-filter.tsx +++ b/apps/dashboard/app/(app)/audit/components/controls/components/logs-filters/components/root-keys-filter.tsx @@ -1,5 +1,6 @@ import { useFilters } from "@/app/(app)/audit/hooks/use-filters"; import { FilterCheckbox } from "@/components/logs/checkbox/filter-checkbox"; +import { shortenId } from "@/lib/shorten-id"; export const RootKeysFilter = ({ rootKeys, @@ -37,10 +38,5 @@ export const RootKeysFilter = ({ // This is to avoid displaying the full id in the filter. const getRootKeyLabel = (rootKey: { id: string; name: string | null }) => { const id = rootKey.id; - const prefix = id.substring(0, id.indexOf("_") + 1); - const obfuscatedMiddle = - id.substring(id.indexOf("_") + 1, id.indexOf("_") + 5).length > 0 ? "..." : ""; - const nextFour = id.substring(id.indexOf("_") + 1, id.indexOf("_") + 5); - const lastFour = id.substring(id.length - 4); - return rootKey.name ?? prefix + nextFour + obfuscatedMiddle + lastFour; + return rootKey.name ?? shortenId(id); }; diff --git a/apps/dashboard/lib/shorten-id.ts b/apps/dashboard/lib/shorten-id.ts new file mode 100644 index 0000000000..172afa5c8c --- /dev/null +++ b/apps/dashboard/lib/shorten-id.ts @@ -0,0 +1,48 @@ +/** + * Shortens an ID by keeping a specified number of characters from the start and end, + * with customizable separator in between. + */ +export function shortenId( + id: string, + options: { + /** Number of characters to keep from the start (default: 8) */ + startChars?: number; + /** Number of characters to keep from the end (default: 4) */ + endChars?: number; + /** Separator between start and end (default: "...") */ + separator?: string; + /** Minimum length required to apply shortening (default: startChars + endChars + 3) */ + minLength?: number; + } = {}, +): string { + const { + startChars = 8, + endChars = 4, + separator = "...", + minLength = startChars + endChars + 3, + } = options; + + // Validate inputs + if (startChars < 0 || endChars < 0) { + throw new Error("startChars and endChars must be non-negative"); + } + + if (startChars + endChars === 0) { + throw new Error("At least one of startChars or endChars must be greater than 0"); + } + + // Return original if too short to meaningfully shorten + if (id.length <= minLength) { + return id; + } + + // Handle edge case where requested chars exceed ID length + if (startChars + endChars >= id.length) { + return id; + } + + const start = id.substring(0, startChars); + const end = id.substring(id.length - endChars); + + return `${start}${separator}${end}`; +}