import { useState } from "react"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { Key, Plus, Trash2, Copy, Check, Code2, Sparkles } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Badge } from "@/components/ui/badge"; import { useLang } from "@/contexts/LanguageContext"; import { useAuth } from "@/contexts/AuthContext"; import { AuthModal } from "@/components/AuthModal"; const BASE = import.meta.env.BASE_URL.replace(/\/$/, ""); const API_BASE = `${BASE}/api`; const V1_BASE = window.location.origin + `${BASE}/api/v1`; interface ApiKeyRecord { id: number; name: string; keyPrefix: string; createdAt: string; lastUsedAt: string | null; } async function fetchKeys(): Promise { const res = await fetch(`${API_BASE}/apikeys`, { credentials: "include" }); if (!res.ok) throw new Error("Failed to fetch keys"); const data = await res.json(); return data.keys; } async function createKey(name: string): Promise<{ key: string; id: number; name: string; keyPrefix: string; createdAt: string }> { const res = await fetch(`${API_BASE}/apikeys`, { method: "POST", credentials: "include", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name }), }); if (!res.ok) throw new Error("Failed to create key"); return res.json(); } async function revokeKey(id: number): Promise { const res = await fetch(`${API_BASE}/apikeys/${id}`, { method: "DELETE", credentials: "include" }); if (!res.ok) throw new Error("Failed to revoke key"); } function CopyButton({ text }: { text: string }) { const [copied, setCopied] = useState(false); const { t } = useLang(); return ( ); } function KeyCard({ apiKey, onRevoke, isRevoking }: { apiKey: ApiKeyRecord; onRevoke: (id: number) => void; isRevoking: boolean }) { const { t } = useLang(); const created = new Date(apiKey.createdAt).toLocaleDateString(); const lastUsed = apiKey.lastUsedAt ? new Date(apiKey.lastUsedAt).toLocaleDateString() : t.apiKeysNeverUsed; return (
{apiKey.name}
{apiKey.keyPrefix}
{t.apiKeysCreatedAt}: {created} {t.apiKeysLastUsed}: {lastUsed}
); } function NewKeyBanner({ rawKey }: { rawKey: string }) { const { t } = useLang(); return (
{t.apiKeysCopyHint}
{rawKey}
); } function KeysContent() { const { t } = useLang(); const qc = useQueryClient(); const [name, setName] = useState(""); const [newRawKey, setNewRawKey] = useState(null); const { data: keys = [], isLoading } = useQuery({ queryKey: ["apikeys"], queryFn: fetchKeys }); const createMutation = useMutation({ mutationFn: createKey, onSuccess: (data) => { setNewRawKey(data.key); setName(""); qc.invalidateQueries({ queryKey: ["apikeys"] }); }, }); const revokeMutation = useMutation({ mutationFn: revokeKey, onSuccess: () => { setNewRawKey(null); qc.invalidateQueries({ queryKey: ["apikeys"] }); }, }); const handleCreate = (e: React.FormEvent) => { e.preventDefault(); createMutation.mutate(name.trim() || "Default Key"); }; return (
{newRawKey && }
setName(e.target.value)} placeholder={t.apiKeysCreateNamePlaceholder} className="flex-1 bg-background/50 border-border/60" maxLength={64} />
{isLoading ? (
Loading...
) : keys.length === 0 ? (
{t.apiKeysEmpty}
{t.apiKeysEmptyHint}
) : (
{keys.map((k) => ( revokeMutation.mutate(id)} isRevoking={revokeMutation.isPending} /> ))}
)}
); } function DocsSection() { const { t } = useLang(); const exampleCode = `from openai import OpenAI client = OpenAI( api_key="sk-sf-...", base_url="${V1_BASE}", ) response = client.images.generate( model="grok", prompt="a glowing neon cityscape at night", n=1, size="1024x1024", ) print(response.data[0].url)`; return (

{t.apiKeysDocsTitle}

{t.apiKeysBaseUrl}
{V1_BASE}
{t.apiKeysEndpointsTitle}
{[ { method: "GET", path: "/models", desc: t.apiKeysModelsDesc }, { method: "POST", path: "/images/generations", desc: t.apiKeysGenerateDesc }, ].map(({ method, path, desc }) => (
{method} {path} {desc}
))}
{t.apiKeysExampleTitle} (Python)
{exampleCode}
); } export function ApiKeys() { const { t } = useLang(); const { isSignedIn, isLoaded } = useAuth(); const [authOpen, setAuthOpen] = useState(false); return (

{t.apiKeysTitle}

{t.apiKeysSubtitle}

{isLoaded && isSignedIn && } {isLoaded && !isSignedIn && (

{t.apiKeysSignInRequired}

)}
); }