add virtual api key validation in verifySession

This commit is contained in:
miloschwartz
2026-08-12 10:38:08 -04:00
parent ac3402a8b3
commit 49020fa6ea
29 changed files with 852 additions and 197 deletions
+1 -1
View File
@@ -18,7 +18,7 @@ import { cn } from "@app/lib/cn";
import {
AI_PROVIDER_AUTH_TYPES,
type AiProviderAuthType
} from "@server/lib/aiProviderDefaults";
} from "@app/lib/aiProviderDefaults";
import { CheckIcon, ChevronsUpDown } from "lucide-react";
import { useTranslations } from "next-intl";
import { useMemo, useState } from "react";
@@ -1,7 +1,7 @@
"use client";
import { MultiSelectTagInput } from "@app/components/multi-select/multi-select-tag-input";
import { AI_CAPABILITIES, type AiCapability } from "@server/lib/aiCapabilities";
import { AI_CAPABILITIES, type AiCapability } from "@app/lib/aiCapabilities";
import { useTranslations } from "next-intl";
import { useMemo, useState } from "react";
+1 -1
View File
@@ -16,7 +16,7 @@ import {
} from "@app/components/ui/popover";
import { cn } from "@app/lib/cn";
import { aiProviderTypeValues } from "@app/lib/aiProviderFormSchema";
import type { AiProviderType } from "@server/lib/aiProviderDefaults";
import type { AiProviderType } from "@app/lib/aiProviderDefaults";
import { CheckIcon, ChevronsUpDown } from "lucide-react";
import { useTranslations } from "next-intl";
import { useMemo, useState } from "react";
+7 -1
View File
@@ -42,6 +42,7 @@ import { Checkbox } from "@app/components/ui/checkbox";
import { useTranslations } from "next-intl";
import { UserSelector, type SelectedUser } from "@app/components/user-selector";
import type { CreateOrEditVirtualApiKeyResponse } from "@server/routers/virtualApiKey/types";
import { formatVirtualApiKeyCredential } from "@app/lib/virtualApiKeyFormat";
import {
MultiResourcesSelector,
formatMultiResourcesSelectorLabel
@@ -146,7 +147,12 @@ export default function CreateVirtualApiKeyForm({
if (res?.data.data.virtualApiKey) {
const key = res.data.data.virtualApiKey;
if (key.secret) {
setCredential(`vk-${key.virtualApiKeyId}.${key.secret}`);
setCredential(
formatVirtualApiKeyCredential(
key.virtualApiKeyId,
key.secret
)
);
}
const resourceLookup = new Map(
+5 -1
View File
@@ -40,6 +40,7 @@ import { Checkbox } from "@app/components/ui/checkbox";
import { useTranslations } from "next-intl";
import { UserSelector, type SelectedUser } from "@app/components/user-selector";
import type { CreateOrEditVirtualApiKeyResponse } from "@server/routers/virtualApiKey/types";
import { formatVirtualApiKeyCredential } from "@app/lib/virtualApiKeyFormat";
import {
MultiResourcesSelector,
formatMultiResourcesSelectorLabel
@@ -151,7 +152,10 @@ export default function EditVirtualApiKeyForm({
const secret = res.data.data.virtualApiKey.secret;
if (secret) {
setCredential(
`vk-${virtualApiKey.virtualApiKeyId}.${secret}`
formatVirtualApiKeyCredential(
virtualApiKey.virtualApiKeyId,
secret
)
);
} else {
toast({
+9 -7
View File
@@ -27,6 +27,10 @@ import type {
ListMyVirtualApiKeysResponse,
VirtualApiKeyWithResources
} from "@server/routers/virtualApiKey/types";
import {
formatVirtualApiKeyCredential,
formatVirtualApiKeyPreview
} from "@app/lib/virtualApiKeyFormat";
type UserVirtualApiKeysProps = {
orgId: string;
@@ -34,10 +38,6 @@ type UserVirtualApiKeysProps = {
initialData: ListMyVirtualApiKeysResponse;
};
function keyPreview(virtualApiKeyId: string, lastChars: string): string {
return `vk-${virtualApiKeyId}••••${lastChars}`;
}
function useRevealSecret(orgId: string, virtualApiKeyId: string) {
const t = useTranslations();
const api = createApiClient(useEnvContext());
@@ -56,7 +56,9 @@ function useRevealSecret(orgId: string, virtualApiKeyId: string) {
.then((res) => {
const secret = res.data.data.virtualApiKey.secret;
if (secret) {
setCredential(`vk-${virtualApiKeyId}.${secret}`);
setCredential(
formatVirtualApiKeyCredential(virtualApiKeyId, secret)
);
} else {
toast({
variant: "destructive",
@@ -95,7 +97,7 @@ function OwnedKeySecret({
lastChars: string;
}) {
const t = useTranslations();
const preview = keyPreview(virtualApiKeyId, lastChars);
const preview = formatVirtualApiKeyPreview(virtualApiKeyId, lastChars);
const { credential, loading, revealSecret } = useRevealSecret(
orgId,
virtualApiKeyId
@@ -137,7 +139,7 @@ function IdentityKeyCenterpiece({
resourceGuid?: string;
}) {
const t = useTranslations();
const preview = keyPreview(virtualApiKeyId, lastChars);
const preview = formatVirtualApiKeyPreview(virtualApiKeyId, lastChars);
const { credential, loading, revealSecret } = useRevealSecret(
orgId,
virtualApiKeyId
+7 -1
View File
@@ -19,6 +19,7 @@ import { createApiClient, formatAxiosError } from "@app/lib/api";
import { useEnvContext } from "@app/hooks/useEnvContext";
import { toast } from "@app/hooks/useToast";
import type { GetVirtualApiKeyResponse } from "@server/routers/virtualApiKey/types";
import { formatVirtualApiKeyCredential } from "@app/lib/virtualApiKeyFormat";
type ViewVirtualApiKeySecretProps = {
open: boolean;
@@ -56,7 +57,12 @@ export default function ViewVirtualApiKeySecret({
}
const key = res.data.data.virtualApiKey;
if (key.secret) {
setCredential(`vk-${key.virtualApiKeyId}.${key.secret}`);
setCredential(
formatVirtualApiKeyCredential(
key.virtualApiKeyId,
key.secret
)
);
} else {
toast({
variant: "destructive",
+8 -2
View File
@@ -46,6 +46,10 @@ import {
import { cn } from "@app/lib/cn";
import { dataTableFilterPopoverContentClassName } from "@app/lib/dataTableFilterPopover";
import type { GetVirtualApiKeyResponse } from "@server/routers/virtualApiKey/types";
import {
formatVirtualApiKeyCredential,
formatVirtualApiKeyPreview
} from "@app/lib/virtualApiKeyFormat";
import { AxiosResponse } from "axios";
export type VirtualApiKeyRow = CreatedVirtualApiKey;
@@ -507,7 +511,7 @@ function VirtualApiKeySecretCell({
}) {
const t = useTranslations();
const api = createApiClient(useEnvContext());
const preview = `vk-${virtualApiKeyId}••••${lastChars}`;
const preview = formatVirtualApiKeyPreview(virtualApiKeyId, lastChars);
const [credential, setCredential] = useState<string | null>(null);
useEffect(() => {
@@ -522,7 +526,9 @@ function VirtualApiKeySecretCell({
}
const secret = res.data.data.virtualApiKey.secret;
if (secret) {
setCredential(`vk-${virtualApiKeyId}.${secret}`);
setCredential(
formatVirtualApiKeyCredential(virtualApiKeyId, secret)
);
}
})
.catch((e) => {
@@ -20,6 +20,10 @@ import type {
GetMyVirtualApiKeyResponse,
VirtualApiKeyWithResources
} from "@server/routers/virtualApiKey/types";
import {
formatVirtualApiKeyCredential,
formatVirtualApiKeyPreview
} from "@app/lib/virtualApiKeyFormat";
import { useQuery } from "@tanstack/react-query";
import type { AxiosResponse } from "axios";
import { Loader2 } from "lucide-react";
@@ -31,10 +35,6 @@ type LauncherInferenceApiKeysSectionProps = {
resourceGuid: string;
};
function keyPreview(virtualApiKeyId: string, lastChars: string): string {
return `vk-${virtualApiKeyId}••••${lastChars}`;
}
function useRevealSecret(orgId: string, virtualApiKeyId: string) {
const t = useTranslations();
const api = createApiClient(useEnvContext());
@@ -53,7 +53,9 @@ function useRevealSecret(orgId: string, virtualApiKeyId: string) {
.then((res) => {
const secret = res.data.data.virtualApiKey.secret;
if (secret) {
setCredential(`vk-${virtualApiKeyId}.${secret}`);
setCredential(
formatVirtualApiKeyCredential(virtualApiKeyId, secret)
);
} else {
toast({
variant: "destructive",
@@ -92,7 +94,7 @@ function PanelKeySecret({
lastChars: string;
}) {
const t = useTranslations();
const preview = keyPreview(virtualApiKeyId, lastChars);
const preview = formatVirtualApiKeyPreview(virtualApiKeyId, lastChars);
const { credential, loading, revealSecret } = useRevealSecret(
orgId,
virtualApiKeyId
+1 -1
View File
@@ -46,7 +46,7 @@ export function UserSelector({
const [debouncedValue] = useDebounce(userSearchQuery, 150);
const { data: users = [] } = useQuery(
orgQueries.users({ orgId, perPage: 10, term: debouncedValue })
orgQueries.users({ orgId, perPage: 10, query: debouncedValue })
);
const usersShown = useMemo(() => {