"use client"; import { useState, useEffect, useCallback } from "react"; import { Key, Plus, Copy, Check, Trash2, AlertTriangle, RefreshCw, Shield } from "lucide-react"; import { cn } from "@/lib/utils"; interface ApiKey { id: string; name: string; keyPrefix: string; createdAt: string; lastUsedAt: string | null; } interface NewKeyResponse extends ApiKey { key: string; } export default function ApiKeysPage() { const [keys, setKeys] = useState([]); const [isLoading, setIsLoading] = useState(true); const [isCreating, setIsCreating] = useState(false); const [showCreateForm, setShowCreateForm] = useState(false); const [newKeyName, setNewKeyName] = useState(""); const [newlyCreatedKey, setNewlyCreatedKey] = useState(null); const [copiedField, setCopiedField] = useState(null); const [revokingId, setRevokingId] = useState(null); const [confirmRevokeId, setConfirmRevokeId] = useState(null); const fetchKeys = useCallback(async () => { setIsLoading(true); try { const res = await fetch("/api/keys", { cache: "no-store" }); if (res.ok) setKeys(await res.json()); } catch (e) { console.error("Failed to fetch:", e); } finally { setIsLoading(false); } }, []); useEffect(() => { fetchKeys(); }, [fetchKeys]); const copyToClipboard = async (text: string, field: string) => { try { await navigator.clipboard.writeText(text); setCopiedField(field); setTimeout(() => setCopiedField(null), 2000); } catch {} }; const handleCreate = async () => { setIsCreating(true); try { const res = await fetch("/api/keys", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name: newKeyName.trim() || undefined }) }); if (res.ok) { const data: NewKeyResponse = await res.json(); setNewlyCreatedKey(data); setShowCreateForm(false); setNewKeyName(""); fetchKeys(); } } catch (e) { console.error("Failed to create:", e); } finally { setIsCreating(false); } }; const handleRevoke = async (id: string) => { setRevokingId(id); try { const res = await fetch(`/api/keys/${id}`, { method: "DELETE" }); if (res.ok) { setConfirmRevokeId(null); fetchKeys(); } } catch (e) { console.error("Failed to revoke:", e); } finally { setRevokingId(null); } }; const formatDate = (d: string) => new Date(d).toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" }); return (

API Keys

Manage API keys for programmatic access

{newlyCreatedKey && (

API Key Created

{newlyCreatedKey.name}

{newlyCreatedKey.key}

This key won't be shown again. Copy it now.

)} {showCreateForm && !newlyCreatedKey && (

Create New API Key

setNewKeyName(e.target.value)} placeholder="e.g. Production, Staging" className="w-full px-4 py-2.5 bg-neutral-950 border border-neutral-800 rounded-lg text-sm text-neutral-200 placeholder:text-neutral-600 focus:outline-none focus:border-blue-500/40 focus:ring-1 focus:ring-blue-500/20 transition-colors" onKeyDown={(e) => { if (e.key === "Enter") handleCreate(); }} autoFocus />
)}

Active Keys

{isLoading ? (
{Array.from({ length: 3 }).map((_, i) => (
))}
) : keys.length === 0 ? (

No API keys yet

Create one for programmatic access

) : (
{keys.map((apiKey) => (

{apiKey.name}

{apiKey.keyPrefix}•••••••• Created {formatDate(apiKey.createdAt)} {apiKey.lastUsedAt && Last used {formatDate(apiKey.lastUsedAt)}}
{confirmRevokeId === apiKey.id ? (
) : ( )}
))}
)}
); }