'use client' import { useState } from 'react' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { Button } from '@/components/ui/button' import { Badge } from '@/components/ui/badge' import { ScrollArea } from '@/components/ui/scroll-area' import { Copy, Key, MoreVertical, Loader2 } from 'lucide-react' import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from '@/components/ui/dialog' import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu' import { Input } from '@/components/ui/input' import { useToast } from '@/hooks/use-toast' import { useMutation, useQuery } from '@tanstack/react-query' import httpBrowserClient from '@/lib/httpBrowserClient' import { ApiEndpoints } from '@/config/api' import { Spinner } from '@/components/ui/spinner' export default function ApiKeys() { const { isPending, error, data: apiKeys, refetch: refetchApiKeys, } = useQuery({ queryKey: ['apiKeys'], queryFn: () => httpBrowserClient .get(ApiEndpoints.auth.listApiKeys()) .then((res) => res.data), // select: (res) => res.data, }) const { toast } = useToast() const [selectedKey, setSelectedKey] = useState<(typeof apiKeys)[0] | null>( null ) const [isRevokeDialogOpen, setIsRevokeDialogOpen] = useState(false) const [isRenameDialogOpen, setIsRenameDialogOpen] = useState(false) const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false) const [newKeyName, setNewKeyName] = useState('') const { mutate: revokeApiKey, isPending: isRevokingApiKey, error: revokeApiKeyError, isSuccess: isRevokeApiKeySuccess, } = useMutation({ mutationFn: (id: string) => httpBrowserClient.post(ApiEndpoints.auth.revokeApiKey(id)), onSuccess: () => { setIsRevokeDialogOpen(false) toast({ title: `API key "${selectedKey.apiKey}" has been revoked`, }) refetchApiKeys() }, onError: () => { toast({ variant: 'destructive', title: 'Error revoking API key', description: revokeApiKeyError?.message, }) }, }) const { mutate: deleteApiKey, isPending: isDeletingApiKey, error: deleteApiKeyError, isSuccess: isDeleteApiKeySuccess, } = useMutation({ mutationFn: (id: string) => httpBrowserClient.delete(ApiEndpoints.auth.deleteApiKey(id)), onSuccess: () => { setIsDeleteDialogOpen(false) toast({ title: `API key deleted`, }) refetchApiKeys() }, onError: () => { toast({ variant: 'destructive', title: 'Error deleting API key', description: deleteApiKeyError?.message, }) }, }) const { mutate: renameApiKey, isPending: isRenamingApiKey, error: renameApiKeyError, isSuccess: isRenameApiKeySuccess, } = useMutation({ mutationFn: ({ id, name }: { id: string; name: string }) => httpBrowserClient.patch(ApiEndpoints.auth.renameApiKey(id), { name }), onSuccess: () => { setIsRenameDialogOpen(false) toast({ title: `API key renamed to "${newKeyName}"`, }) refetchApiKeys() }, onError: () => { toast({ variant: 'destructive', title: 'Error renaming API key', description: renameApiKeyError?.message, }) }, }) return ( API Keys
{isPending && (
)} {error && (
Error: {error.message}
)} {!isPending && !error && apiKeys?.data?.length === 0 && (
No API keys found
)} {apiKeys?.data?.map((apiKey) => (

{apiKey.name || 'API Key'}

{apiKey.revokedAt ? 'Revoked' : 'Active'}
{apiKey.apiKey}
Created at:{' '} {new Date(apiKey.createdAt).toLocaleString('en-US', { dateStyle: 'medium', timeStyle: 'short', })}
Last used: {/* if usage count is 0, show never */} {apiKey?.lastUsedAt && apiKey.usageCount > 0 ? new Date(apiKey.lastUsedAt).toLocaleString( 'en-US', { dateStyle: 'medium', timeStyle: 'short', } ) : 'Never'}
{ setSelectedKey(apiKey) setNewKeyName(apiKey.name || 'API Key') setIsRenameDialogOpen(true) }} > Rename { setSelectedKey(apiKey) setIsRevokeDialogOpen(true) }} disabled={!!apiKey.revokedAt} > Revoke { setSelectedKey(apiKey) setIsDeleteDialogOpen(true) }} > Delete
))}
{/* Revoke Dialog */} Revoke API Key Are you sure you want to revoke this API key? This action cannot be undone, and any applications using this key will stop working immediately. {/* Delete Dialog */} Delete API Key Are you sure you want to delete this API key? This action cannot be undone. {/* Rename Dialog */} Rename API Key Enter a new name for your API key. setNewKeyName(e.target.value)} placeholder='Enter new name' />
) }