"use client"; import { useState, useEffect, useMemo, useCallback, memo, useRef, useId } from "react"; import { Card, Button, Input, Modal, CardSkeleton } from "@/shared/components"; import { useCopyToClipboard } from "@/shared/hooks/useCopyToClipboard"; import { useLocale, useTranslations } from "next-intl"; import { getProviderDisplayName } from "@/lib/display/names"; import { compareTr, matchesSearch } from "@/shared/utils/turkishText"; import { ENDPOINT_CATEGORIES } from "@/shared/constants/endpointCategories"; import ApiKeyFilterBar from "./components/ApiKeyFilterBar"; import { isKeyActive, isExpired, isRestricted as isKeyRestricted, classifyKeyStatus, computeApiKeyCounts, formatUsdCost, toLocalDateTimeInputValue, toggleKeyVisibility, } from "./apiManagerPageUtils"; import type { KeyStatus, KeyType } from "./apiManagerPageUtils"; import { readActiveOnlyPreference, writeActiveOnlyPreference } from "./apiManagerPageStorage"; import { buildApiKeyCreateScopes, mergeApiKeyPermissionScopes } from "./apiManagerScopes"; import { SELF_ACCOUNT_QUOTA_SCOPE, SELF_USAGE_SCOPE } from "@/shared/constants/selfServiceScopes"; import { UsageLimitSettings } from "./components/UsageLimitSettings"; // Constants for validation const MAX_KEY_NAME_LENGTH = 200; const MAX_SELECTED_MODELS = 500; const CLAUDE_CODE_DEFAULT_MODEL_ID = "cc/*"; const CLAUDE_CODE_DEFAULT_MODEL_NAME = "Claude Code default"; const CLAUDE_CODE_DEFAULT_FAMILIES = [ { id: "other", label: "other" }, { id: "fable", label: "fable" }, { id: "opus", label: "opus" }, { id: "sonnet", label: "sonnet" }, { id: "haiku", label: "haiku" }, ] as const; type ClaudeCodeFamilyId = (typeof CLAUDE_CODE_DEFAULT_FAMILIES)[number]["id"]; type ClaudeCodeBlockableFamilyId = Exclude; const CLAUDE_CODE_FAMILY_BLOCK_PATTERNS: Record = { fable: ["claude-fable*", "fable"], opus: ["claude-opus*", "opus"], sonnet: ["claude-sonnet*", "sonnet"], haiku: ["claude-haiku*", "haiku"], }; const CLAUDE_CODE_BLOCK_PATTERN_SET = new Set( Object.values(CLAUDE_CODE_FAMILY_BLOCK_PATTERNS).flat() ); // Debounce hook for search optimization function useDebouncedValue(value: T, delay: number): T { const [debouncedValue, setDebouncedValue] = useState(value); useEffect(() => { const timer = setTimeout(() => setDebouncedValue(value), delay); return () => clearTimeout(timer); }, [value, delay]); return debouncedValue; } // Sanitize user input to prevent XSS function sanitizeInput(input: string): string { return input .replace(/[<>]/g, "") .replace(/"/g, "") .replace(/'/g, "") .trim() .slice(0, MAX_KEY_NAME_LENGTH); } // Validate key name function validateKeyName( name: string, t: (key: string, values?: Record) => string ): { valid: boolean; error?: string } { if (!name || !name.trim()) { return { valid: false, error: t("keyNameRequired") }; } if (name.length > MAX_KEY_NAME_LENGTH) { return { valid: false, error: t("keyNameTooLong", { max: MAX_KEY_NAME_LENGTH }) }; } // Allow Unicode letters (accented chars), numbers, spaces, hyphens, underscores if (!/^[\p{L}\p{N}_\-\s]+$/u.test(name)) { return { valid: false, error: t("keyNameInvalid"), }; } return { valid: true }; } interface AccessSchedule { enabled: boolean; from: string; until: string; days: number[]; tz: string; } type StreamDefaultMode = "legacy" | "json"; interface ApiKey { id: string; name: string; key: string; allowedModels: string[] | null; blockedModels?: string[] | null; allowedCombos: string[] | null; allowedConnections: string[] | null; noLog?: boolean; autoResolve?: boolean; isActive?: boolean; throttleDelayMs?: number | null; isBanned?: boolean; expiresAt?: string | null; maxSessions?: number; accessSchedule?: AccessSchedule | null; rateLimits?: Array<{ limit: number; window: number }> | null; scopes?: string[]; allowedEndpoints?: string[]; streamDefaultMode?: StreamDefaultMode; disableNonPublicModels?: boolean; allowUsageCommand?: boolean; usageLimitEnabled?: boolean; dailyUsageLimitUsd?: number | null; weeklyUsageLimitUsd?: number | null; allowedQuotas?: string[] | null; createdAt: string; } interface ProviderConnection { id: string; name: string; provider: string; isActive: boolean; } interface KeyUsageStats { totalRequests: number; totalCost: number; lastUsed: string | null; } interface Model { id: string; owned_by: string; name?: string; } interface ComboOption { id?: string; name: string; models?: unknown[]; } /** Tuple type for models grouped by provider: [providerName, models[]] */ type ProviderGroup = [provider: string, models: Model[]]; function isClaudeCodeModel(model: Model): boolean { return ( model.owned_by === "claude" || model.id.startsWith("cc/") || model.id.startsWith("claude/") ); } function withClaudeCodeDefaultModel(models: Model[]): Model[] { if (!models.some(isClaudeCodeModel)) return models; if (models.some((model) => model.id === CLAUDE_CODE_DEFAULT_MODEL_ID)) return models; return [ { id: CLAUDE_CODE_DEFAULT_MODEL_ID, name: CLAUDE_CODE_DEFAULT_MODEL_NAME, owned_by: "claude", }, ...models, ]; } function getBlockedClaudeCodeFamilies(blockedModels: string[]): ClaudeCodeBlockableFamilyId[] { return (Object.keys(CLAUDE_CODE_FAMILY_BLOCK_PATTERNS) as ClaudeCodeBlockableFamilyId[]).filter( (familyId) => CLAUDE_CODE_FAMILY_BLOCK_PATTERNS[familyId].some((pattern) => blockedModels.includes(pattern)) ); } function isClaudeCodeFamilyModel(modelId: string, familyId: ClaudeCodeBlockableFamilyId): boolean { const normalized = modelId.toLowerCase(); return ( normalized === familyId || normalized.includes(`/${familyId}`) || normalized.includes(`-${familyId}`) ); } export default function ApiManagerPageClient() { const t = useTranslations("apiManager"); const tc = useTranslations("common"); const locale = useLocale(); const newKeyNameInputId = useId(); const createKeyFormRef = useRef(null); const [keys, setKeys] = useState([]); const [allModels, setAllModels] = useState([]); const [allCombos, setAllCombos] = useState([]); const [allConnections, setAllConnections] = useState([]); const [loading, setLoading] = useState(true); const [showAddModal, setShowAddModal] = useState(false); const [newKeyName, setNewKeyName] = useState(""); const [newKeyManageEnabled, setNewKeyManageEnabled] = useState(false); const [newKeySelfUsageEnabled, setNewKeySelfUsageEnabled] = useState(true); const [newKeyAccountQuotaEnabled, setNewKeyAccountQuotaEnabled] = useState(false); const [newKeyAllowUsageCommand, setNewKeyAllowUsageCommand] = useState(false); const [createdKey, setCreatedKey] = useState(null); const [editingKey, setEditingKey] = useState(null); const [showPermissionsModal, setShowPermissionsModal] = useState(false); const [searchModel, setSearchModel] = useState(""); const [pageError, setPageError] = useState(null); const [nameError, setNameError] = useState(null); const [createError, setCreateError] = useState(null); const [isSubmitting, setIsSubmitting] = useState(false); const [usageStats, setUsageStats] = useState>({}); const [sessionCounts, setSessionCounts] = useState>({}); const [allowKeyReveal, setAllowKeyReveal] = useState(false); // Per-row API key visibility toggle (eye / eye-off). Keys default to masked. // Map id -> fully revealed key string fetched on demand from /api/keys/{id}/reveal. const [revealedKeys, setRevealedKeys] = useState>(new Map()); const [visibleKeys, setVisibleKeys] = useState>(new Set()); const createKeyNameFieldRef = useRef(null); const [searchQuery, setSearchQuery] = useState(""); const [activeOnly, setActiveOnly] = useState(false); const [statusFilter, setStatusFilter] = useState(null); const [typeFilter, setTypeFilter] = useState(null); const [quotaPoolGroup, setQuotaPoolGroup] = useState>({}); const { copied, copy } = useCopyToClipboard(); const scrollCreateKeyFormToTop = useCallback(() => { const scrollContainer = createKeyFormRef.current?.parentElement; if (scrollContainer instanceof HTMLElement) { scrollContainer.scrollTop = 0; } const input = document.getElementById(newKeyNameInputId); input?.scrollIntoView({ block: "nearest", inline: "nearest" }); input?.focus({ preventScroll: true }); }, [newKeyNameInputId]); useEffect(() => { fetchData(); fetchModels(); fetchCombos(); fetchConnections(); }, []); useEffect(() => { if (!showAddModal || !nameError) return; requestAnimationFrame(() => { createKeyNameFieldRef.current?.scrollIntoView({ block: "center", behavior: "instant" }); }); }, [nameError, showAddModal]); useEffect(() => { setActiveOnly(readActiveOnlyPreference()); }, []); useEffect(() => { writeActiveOnlyPreference(activeOnly); }, [activeOnly]); useEffect(() => { let cancelled = false; const loadQuotaGroups = async () => { try { const [poolsRes, groupsRes] = await Promise.all([ fetch("/api/quota/pools"), fetch("/api/quota/groups"), ]); if (!poolsRes.ok || !groupsRes.ok) return; const poolsData = await poolsRes.json(); const groupsData = await groupsRes.json(); const pools: Array<{ id: string; groupId: string }> = Array.isArray(poolsData.pools) ? poolsData.pools : []; const groups: Array<{ id: string; name: string }> = Array.isArray(groupsData.groups) ? groupsData.groups : []; const groupNameById: Record = {}; for (const g of groups) { groupNameById[g.id] = g.name; } const map: Record = {}; for (const p of pools) { if (groupNameById[p.groupId]) { map[p.id] = groupNameById[p.groupId]; } } if (!cancelled) setQuotaPoolGroup(map); } catch { // fail open — quota group chips simply won't render } }; loadQuotaGroups(); return () => { cancelled = true; }; }, []); useEffect(() => { if (!showAddModal || !nameError) return; const timeout = window.setTimeout(() => { scrollCreateKeyFormToTop(); }, 0); return () => window.clearTimeout(timeout); }, [showAddModal, nameError, scrollCreateKeyFormToTop]); const fetchModels = async () => { try { const res = await fetch("/v1/models"); if (res.ok) { const data = await res.json(); setAllModels(data.data || []); } } catch (error) { console.log("Error fetching models:", error); } }; const fetchCombos = async () => { try { const res = await fetch("/api/combos"); if (res.ok) { const data = await res.json(); const combos = Array.isArray(data.combos) ? data.combos : []; setAllCombos( combos.filter((combo: any) => typeof combo?.name === "string" && combo.name.trim()) ); } } catch (error) { console.log("Error fetching combos:", error); } }; const fetchConnections = async () => { try { const res = await fetch("/api/providers"); if (res.ok) { const data = await res.json(); setAllConnections(data.connections || []); } } catch (error) { console.log("Error fetching connections:", error); } }; const fetchData = async () => { try { const res = await fetch("/api/keys"); if (res.ok) { const data = await res.json(); setKeys(data.keys || []); setAllowKeyReveal(data.allowKeyReveal === true); // Fetch usage stats after keys are loaded fetchUsageStats(data.keys || []); fetchSessionCounts(data.keys || []); } } catch (error) { console.log("Error fetching keys:", error); } finally { setLoading(false); } }; const fetchUsageStats = async (apiKeys: ApiKey[]) => { if (apiKeys.length === 0) return; try { // Fetch analytics (accurate aggregated counts) and recent call-logs // (for lastUsed timestamps) in parallel. // The previous approach matched call-logs by key.id === log.apiKeyId, // but these use different ID schemes and never matched, yielding 0. const [analyticsRes, logsRes] = await Promise.all([ fetch("/api/usage/analytics?range=all"), fetch("/api/usage/call-logs?limit=1000"), ]); const analytics = analyticsRes.ok ? await analyticsRes.json() : null; const byApiKey: any[] = analytics?.byApiKey || []; const logs = logsRes.ok ? await logsRes.json() : []; const stats: Record = {}; for (const key of apiKeys) { // Match analytics entry by unique API Key ID (isolates usage to this specific key instance) const matches = byApiKey.filter((entry: any) => entry.apiKeyId === key.id); const totalRequests = matches.reduce( (sum: number, entry: any) => sum + (Number(entry.requests) || 0), 0 ); const totalCost = matches.reduce((sum: number, entry: any) => { const cost = Number(entry.cost); return sum + (Number.isFinite(cost) ? cost : 0); }, 0); // Match call logs by unique ID as well for the lastUsed timestamp // Prefer an exact apiKeyId match; fall back to name match for legacy // logs that predate per-key IDs (apiKeyId absent). const lastUsed = (logs || []).find( (log: any) => log.apiKeyId === key.id || (!log.apiKeyId && log.apiKeyName === key.name) )?.timestamp || null; stats[key.id] = { totalRequests, totalCost, lastUsed, }; } setUsageStats(stats); } catch (e) { console.log("Error fetching usage stats:", e); } }; const fetchSessionCounts = async (apiKeys: ApiKey[]) => { if (apiKeys.length === 0) { setSessionCounts({}); return; } try { const res = await fetch("/api/sessions"); if (!res.ok) return; const data = await res.json(); const byApiKeyRaw = data && typeof data.byApiKey === "object" && !Array.isArray(data.byApiKey) ? data.byApiKey : {}; const normalized: Record = {}; for (const key of apiKeys) { const value = byApiKeyRaw[key.id]; normalized[key.id] = typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 0; } setSessionCounts(normalized); } catch (error) { console.log("Error fetching session counts:", error); } }; const clearPageError = useCallback(() => setPageError(null), []); const keyCounts = useMemo(() => computeApiKeyCounts(keys), [keys]); const filteredKeys = useMemo(() => { let list = keys; // 1. activeOnly toggle (shortcut for the most common case) if (activeOnly) { list = list.filter(isKeyActive); } // 2. status chip filter if (statusFilter === "active") list = list.filter(isKeyActive); else if (statusFilter === "disabled") list = list.filter((k) => k.isActive === false); else if (statusFilter === "banned") list = list.filter((k) => k.isBanned === true); else if (statusFilter === "expired") list = list.filter(isExpired); // 3. type chip filter if (typeFilter === "manage") list = list.filter((k) => k.scopes?.includes("manage")); else if (typeFilter === "restricted") list = list.filter(isKeyRestricted); else if (typeFilter === "standard") list = list.filter((k) => !k.scopes?.includes("manage") && !isKeyRestricted(k)); // 4. search query (case-insensitive substring on name and key) if (searchQuery.trim()) { const q = searchQuery.toLowerCase(); list = list.filter( (k) => k.name.toLowerCase().includes(q) || k.key.toLowerCase().includes(q) ); } return list; }, [keys, activeOnly, statusFilter, typeFilter, searchQuery]); const isFiltered = activeOnly || statusFilter !== null || typeFilter !== null || searchQuery.trim() !== ""; const isQuotaKey = (k: ApiKey) => Array.isArray(k.allowedQuotas) && k.allowedQuotas.length > 0; const quotaKeys = filteredKeys.filter(isQuotaKey); const normalKeys = filteredKeys.filter((k) => !isQuotaKey(k)); const permissionModels = useMemo(() => withClaudeCodeDefaultModel(allModels), [allModels]); const quotaGroupsForKey = (k: ApiKey): string[] => { if (!Array.isArray(k.allowedQuotas)) return []; const seen = new Set(); const result: string[] = []; for (const poolId of k.allowedQuotas) { const groupName = quotaPoolGroup[poolId]; if (groupName && !seen.has(groupName)) { seen.add(groupName); result.push(groupName); } } return result; }; const handleClearFilters = () => { setSearchQuery(""); setActiveOnly(false); setStatusFilter(null); setTypeFilter(null); }; const handleCreateKey = async () => { // Validate raw input first, then sanitize const validation = validateKeyName(newKeyName, t); if (!validation.valid) { scrollCreateKeyFormToTop(); setNameError(validation.error || t("invalidKeyName")); return; } const sanitizedName = sanitizeInput(newKeyName); setIsSubmitting(true); setNameError(null); setCreateError(null); try { const res = await fetch("/api/keys", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name: sanitizedName, scopes: buildApiKeyCreateScopes({ manageEnabled: newKeyManageEnabled, selfUsageEnabled: newKeySelfUsageEnabled, selfAccountQuotaEnabled: newKeyAccountQuotaEnabled, }), allowUsageCommand: newKeyAllowUsageCommand, }), }); const data = await res.json(); if (res.ok) { setCreatedKey(data.key); await fetchData(); setNewKeyName(""); setNewKeyManageEnabled(false); setNewKeySelfUsageEnabled(true); setNewKeyAccountQuotaEnabled(false); setNewKeyAllowUsageCommand(false); setShowAddModal(false); } else { setCreateError(data.error || t("failedCreateKey")); } } catch (error) { console.error("Error creating key:", error); setCreateError(t("failedCreateKeyRetry")); } finally { setIsSubmitting(false); } }; const handleDeleteKey = async (id: string) => { if (!id || typeof id !== "string" || !/^[a-zA-Z0-9_-]+$/.test(id)) { setPageError(t("invalidKeyId")); return; } if (!confirm(t("deleteConfirm"))) return; setIsSubmitting(true); clearPageError(); try { const res = await fetch(`/api/keys/${encodeURIComponent(id)}`, { method: "DELETE" }); if (res.ok) { setKeys((prev) => prev.filter((k) => k.id !== id)); // Clean up any cached reveal/visibility state for this key. setRevealedKeys((prev) => { if (!prev.has(id)) return prev; const next = new Map(prev); next.delete(id); return next; }); setVisibleKeys((prev) => (prev.has(id) ? toggleKeyVisibility(prev, id) : prev)); } else { const data = await res.json(); setPageError(data.error || t("failedDeleteKey")); } } catch (error) { console.error("Error deleting key:", error); setPageError(t("failedDeleteKeyRetry")); } finally { setIsSubmitting(false); } }; const handleRegenerateKey = async (id: string) => { if (!id) return; if (!confirm(t("regenerateConfirm"))) return; setIsSubmitting(true); clearPageError(); try { const res = await fetch(`/api/keys/${encodeURIComponent(id)}/regenerate`, { method: "POST" }); const data = await res.json(); if (res.ok) { setCreatedKey(data.key); await fetchData(); } else { setPageError(data.error || t("failedRegenerateKey")); } } catch (error) { console.error("Error regenerating key:", error); setPageError(t("failedRegenerateKeyRetry")); } finally { setIsSubmitting(false); } }; const handleOpenPermissions = (key: ApiKey) => { if (!key || !key.id) return; setEditingKey(key); setShowPermissionsModal(true); }; const handleCopyExistingKey = async (keyId: string) => { if (!keyId) return; try { const res = await fetch(`/api/keys/${encodeURIComponent(keyId)}/reveal`); if (!res.ok) { console.log("Error revealing key:", await res.text()); return; } const data = await res.json(); if (typeof data?.key === "string") { // Cache the revealed value so a subsequent show-toggle does not refetch. setRevealedKeys((prev) => { const next = new Map(prev); next.set(keyId, data.key); return next; }); await copy(data.key, `existing_key_${keyId}`); } } catch (error) { console.log("Error copying existing key:", error); } }; /** * Toggle the visibility of one key inline (eye / eye-off button). * Lazy-fetches the full key from /api/keys/{id}/reveal on the FIRST show, * then caches it in `revealedKeys` so re-toggling is instant. Hiding only * flips the visibility set — the cached reveal stays so a re-show is free. */ const handleToggleKeyVisibility = async (keyId: string) => { if (!keyId) return; const isCurrentlyVisible = visibleKeys.has(keyId); if (!isCurrentlyVisible && !revealedKeys.has(keyId)) { try { const res = await fetch(`/api/keys/${encodeURIComponent(keyId)}/reveal`); if (!res.ok) { console.log("Error revealing key:", await res.text()); return; } const data = await res.json(); if (typeof data?.key !== "string") return; setRevealedKeys((prev) => { const next = new Map(prev); next.set(keyId, data.key); return next; }); } catch (error) { console.log("Error revealing key:", error); return; } } setVisibleKeys((prev) => toggleKeyVisibility(prev, keyId)); }; const handleUpdatePermissions = async ( name: string, allowedModels: string[], allowedCombos: string[], noLog: boolean, allowedConnections: string[], autoResolve: boolean, isActive: boolean, throttleDelayMs: number, isBanned: boolean, expiresAt: string | null, maxSessions: number, accessSchedule: AccessSchedule | null, rateLimits: Array<{ limit: number; window: number }> | null, scopes: string[], allowedEndpoints: string[], streamDefaultMode: StreamDefaultMode, disableNonPublicModels: boolean, allowUsageCommand: boolean, usageLimitEnabled: boolean, dailyUsageLimitUsd: number | null, weeklyUsageLimitUsd: number | null, blockedModels: string[] ) => { if (!editingKey || !editingKey.id) return; const sanitizedName = sanitizeInput(name); // Validate models array if (!Array.isArray(allowedModels)) { return; } // Limit number of selected models to prevent abuse if (allowedModels.length > MAX_SELECTED_MODELS) { return; } // Validate each model ID const validModels = allowedModels.filter( (id) => typeof id === "string" && id.length > 0 && id.length < 200 ); const validBlockedModels = blockedModels.filter( (id) => typeof id === "string" && id.length > 0 && id.length < 200 ); const validCombos = allowedCombos.filter( (name) => typeof name === "string" && name.trim().length > 0 && name.length < 200 ); // Validate connections (must be UUIDs) const validConnections = allowedConnections.filter( (id) => typeof id === "string" && /^[0-9a-f-]{36}$/i.test(id) ); const normalizedMaxSessions = typeof maxSessions === "number" && Number.isFinite(maxSessions) ? Math.max(0, Math.floor(maxSessions)) : 0; const normalizedThrottleDelayMs = typeof throttleDelayMs === "number" && Number.isFinite(throttleDelayMs) ? Math.max(0, Math.min(300000, Math.floor(throttleDelayMs))) : 0; setIsSubmitting(true); clearPageError(); try { const res = await fetch(`/api/keys/${encodeURIComponent(editingKey.id)}`, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name: sanitizedName, allowedModels: validModels, blockedModels: validBlockedModels, allowedCombos: validCombos, allowedConnections: validConnections, noLog, autoResolve, isActive, throttleDelayMs: normalizedThrottleDelayMs, isBanned, expiresAt, maxSessions: normalizedMaxSessions, accessSchedule, rateLimits, scopes, allowedEndpoints, streamDefaultMode, disableNonPublicModels, allowUsageCommand, usageLimitEnabled, dailyUsageLimitUsd, weeklyUsageLimitUsd, }), }); if (res.ok) { await fetchData(); setShowPermissionsModal(false); setEditingKey(null); } else { const data = await res.json(); setPageError(data.error || t("failedUpdatePermissions")); } } catch (error) { console.error("Error updating permissions:", error); setPageError(t("failedUpdatePermissionsRetry")); } finally { setIsSubmitting(false); } }; // Debounced search for performance const debouncedSearchModel = useDebouncedValue(searchModel, 150); // Group models by provider (issue #2021 — use centralized display helper so // custom OpenAI-/Anthropic-compatible providers don't leak raw synthetic // ids like "openai-compatible-chat-" into the grouping label) const modelsByProvider = useMemo((): ProviderGroup[] => { const grouped: Record = {}; for (const model of permissionModels) { const provider = getProviderDisplayName(model.owned_by) || model.owned_by || t("unknownProvider"); if (!grouped[provider]) grouped[provider] = []; grouped[provider].push(model); } return Object.entries(grouped).sort((a, b) => compareTr(a[0], b[0])); }, [permissionModels, t]); // Filter models based on debounced search const filteredModelsByProvider = useMemo((): ProviderGroup[] => { if (!debouncedSearchModel.trim()) return modelsByProvider; return modelsByProvider .map( ([provider, models]): ProviderGroup => [ provider, models.filter( (m) => matchesSearch(m.id, debouncedSearchModel) || matchesSearch(m.name || "", debouncedSearchModel) || matchesSearch(provider, debouncedSearchModel) ), ] ) .filter(([, models]) => models.length > 0); }, [modelsByProvider, debouncedSearchModel]); if (loading) { return (
); } return (
{/* Error Banner */} {pageError && (
error

{pageError}

)} {/* Filter Bar — shown when there are keys */} {keys.length > 0 && ( )} {/* Keys List Card */}
vpn_key

{t("registeredKeys")} {isFiltered && ( ({t("shownOf", { shown: filteredKeys.length, total: keys.length })}) )} {!isFiltered && ( ({keys.length}) )}

{keys.length === 1 ? t("keyRegistered", { count: keys.length }) : t("keysRegistered", { count: keys.length })}

{t("keysSecurityNote")}

{keys.length === 0 ? (
vpn_key

{t("noKeys")}

{t("noKeysDesc")}

) : filteredKeys.length === 0 ? (
search_off

{t("emptyFilterTitle")}

) : ( (() => { const renderKeyRow = (key: ApiKey) => { const stats = usageStats[key.id]; const isRestricted = Array.isArray(key.allowedModels) && key.allowedModels.length > 0; const hasComboRestrictions = Array.isArray(key.allowedCombos) && key.allowedCombos.length > 0; const hasConnectionRestrictions = Array.isArray(key.allowedConnections) && key.allowedConnections.length > 0; const noLogEnabled = key.noLog === true; const keyIsActive = key.isActive !== false; // default true const throttleDelayMs = typeof key.throttleDelayMs === "number" && key.throttleDelayMs > 0 ? key.throttleDelayMs : 0; const hasThrottle = throttleDelayMs > 0; const hasManageScope = Array.isArray(key.scopes) && key.scopes.includes("manage"); const hasJsonStreamDefault = key.streamDefaultMode === "json"; const hasLocalUsageCommand = key.allowUsageCommand === true; const maxSessions = typeof key.maxSessions === "number" ? key.maxSessions : 0; const hasSessionLimit = maxSessions > 0; const activeSessions = sessionCounts[key.id] || 0; const hasSchedule = key.accessSchedule?.enabled === true; const keyIsQuota = isQuotaKey(key); const groups = quotaGroupsForKey(key); const visibleGroups = groups.slice(0, 3); const extraGroupCount = groups.length - visibleGroups.length; return (
{isRestricted ? "lock" : "lock_open"} {key.name}
{visibleKeys.has(key.id) ? (revealedKeys.get(key.id) ?? key.key) : key.key} {allowKeyReveal ? ( <> ) : ( lock )}
{/* QUOTA differentiation chips — prepended before existing badges */} {keyIsQuota && ( {t("quotaModeOnly")} )} {keyIsQuota && visibleGroups.map((groupName) => ( {groupName} ))} {keyIsQuota && extraGroupCount > 0 && ( +{extraGroupCount} )} {/* Existing badges */} {isRestricted ? ( ) : ( )} {hasConnectionRestrictions && ( )} {hasComboRestrictions && ( )} {noLogEnabled && ( visibility_off No-Log )} {key.autoResolve && ( auto_fix_high Auto-Resolve )} {hasJsonStreamDefault && ( data_object {t("streamDefaultBadge")} )} {hasLocalUsageCommand && ( terminal {t("localUsageCommandBadge")} )} {key.usageLimitEnabled === true && ( paid USD quota )} {hasSessionLimit && ( group Sessions: {activeSessions}/{maxSessions} )} {hasThrottle && ( speed+ {throttleDelayMs}ms )} {hasManageScope && ( admin_panel_settings manage )} {!keyIsActive && ( block {t("disabled")} )} {hasSchedule && ( schedule {t("scheduleActive")} )} {key.isBanned && ( gavel BANNED )} {key.expiresAt && new Date(key.expiresAt).getTime() < Date.now() && ( event_busy EXPIRED )}
{stats?.totalRequests ?? 0}{" "} {t("reqs")} {(stats?.totalRequests ?? 0) > 0 && ( {formatUsdCost(stats?.totalCost ?? 0, locale)} )} {stats?.lastUsed ? ( {t("lastUsedOn", { date: new Date(stats.lastUsed).toLocaleDateString() })} ) : ( {t("neverUsed")} )}
{new Date(key.createdAt).toLocaleDateString()}
payments
); }; const tableHeader = (
{t("name")}
{t("key")}
{t("permissions")}
{t("usage")}
{t("created")}
{t("actions")}
); return (
{normalKeys.length > 0 && (
{/* Normal keys section heading */}
vpn_key {t("normalKeysSection")} {normalKeys.length}
{tableHeader} {normalKeys.map(renderKeyRow)}
)} {quotaKeys.length > 0 && (
{/* Quota keys section heading */}
toll {t("quotaKeysSection")} {quotaKeys.length} {t("quotaPill")}
{tableHeader} {quotaKeys.map(renderKeyRow)}
)}
); })() )}
{/* Add Key Modal */} { setShowAddModal(false); setNewKeyName(""); setNewKeyManageEnabled(false); setNewKeySelfUsageEnabled(true); setNewKeyAccountQuotaEnabled(false); setNewKeyAllowUsageCommand(false); setNameError(null); setCreateError(null); }} >
{ setNewKeyName(e.target.value); setNameError(null); }} placeholder={t("keyNamePlaceholder")} maxLength={MAX_KEY_NAME_LENGTH} error={nameError} autoFocus />

{t("keyNameDesc")}

{t("managementAccess")}

{t("managementAccessDesc")}

{t("selfServiceVisibility")}

{t("selfServiceVisibilityDesc")}

{t("ownUsageVisibility")}

{t("ownUsageVisibilityDesc")}

{t("sharedAccountQuotaVisibility")}

{t("sharedAccountQuotaVisibilityDesc")}

{t("localUsageCommand")}

{t("localUsageCommandDesc")}

{createError && (
error

{createError}

)}
{/* Created Key Modal */} setCreatedKey(null)}>
check_circle

{t("keyCreatedSuccess")}

{t("keyCreatedNote")}

{/* Permissions Modal */} {editingKey && ( { setShowPermissionsModal(false); setEditingKey(null); }} apiKey={editingKey} modelsByProvider={filteredModelsByProvider} allModels={permissionModels} allCombos={allCombos} allConnections={allConnections} searchModel={searchModel} onSearchChange={setSearchModel} onSave={handleUpdatePermissions} /> )}
); } // -- Permissions Modal Component (Memoized for Performance) ------------------------------------------ const PermissionsModal = memo(function PermissionsModal({ isOpen, onClose, apiKey, modelsByProvider, allModels, allCombos, allConnections, searchModel, onSearchChange, onSave, }: { isOpen: boolean; onClose: () => void; apiKey: ApiKey; modelsByProvider: ProviderGroup[]; allModels: Model[]; allCombos: ComboOption[]; allConnections: ProviderConnection[]; searchModel: string; onSearchChange: (v: string) => void; onSave: ( name: string, models: string[], combos: string[], noLog: boolean, connections: string[], autoResolve: boolean, isActive: boolean, throttleDelayMs: number, isBanned: boolean, expiresAt: string | null, maxSessions: number, accessSchedule: AccessSchedule | null, rateLimits: Array<{ limit: number; window: number }> | null, scopes: string[], allowedEndpoints: string[], streamDefaultMode: StreamDefaultMode, disableNonPublicModels: boolean, allowUsageCommand: boolean, usageLimitEnabled: boolean, dailyUsageLimitUsd: number | null, weeklyUsageLimitUsd: number | null, blockedModels: string[] ) => void; }) { const t = useTranslations("apiManager"); const tc = useTranslations("common"); // Initialize state from props - component remounts when key prop changes const initialModels = Array.isArray(apiKey?.allowedModels) ? apiKey.allowedModels : []; const initialBlockedModels = useMemo( () => (Array.isArray(apiKey?.blockedModels) ? apiKey.blockedModels : []), [apiKey?.blockedModels] ); const initialCombos = Array.isArray(apiKey?.allowedCombos) ? apiKey.allowedCombos : []; const initialConnections = Array.isArray(apiKey?.allowedConnections) ? apiKey.allowedConnections : []; const [keyName, setKeyName] = useState(apiKey?.name ?? ""); const [selectedModels, setSelectedModels] = useState(initialModels); const [blockedClaudeCodeFamilies, setBlockedClaudeCodeFamilies] = useState< ClaudeCodeBlockableFamilyId[] >(() => getBlockedClaudeCodeFamilies(initialBlockedModels)); const [claudeCodeFamiliesExpanded, setClaudeCodeFamiliesExpanded] = useState(false); const [selectedCombos, setSelectedCombos] = useState(initialCombos); const [allowAll, setAllowAll] = useState(initialModels.length === 0); const [allowAllCombos, setAllowAllCombos] = useState(initialCombos.length === 0); const [noLogEnabled, setNoLogEnabled] = useState(apiKey?.noLog === true); const [autoResolveEnabled, setAutoResolveEnabled] = useState(apiKey?.autoResolve === true); const [keyIsActive, setKeyIsActive] = useState(apiKey?.isActive !== false); const [throttleDelayMs, setThrottleDelayMs] = useState( typeof apiKey?.throttleDelayMs === "number" && apiKey.throttleDelayMs > 0 ? apiKey.throttleDelayMs : 0 ); const [keyIsBanned, setKeyIsBanned] = useState(apiKey?.isBanned === true); const [expiresAt, setExpiresAt] = useState(apiKey?.expiresAt ?? ""); const [manageEnabled, setManageEnabled] = useState( Array.isArray(apiKey?.scopes) && apiKey.scopes.includes("manage") ); const [selfUsageEnabled, setSelfUsageEnabled] = useState( Array.isArray(apiKey?.scopes) && apiKey.scopes.includes(SELF_USAGE_SCOPE) ); const [selfAccountQuotaEnabled, setSelfAccountQuotaEnabled] = useState( Array.isArray(apiKey?.scopes) && apiKey.scopes.includes(SELF_ACCOUNT_QUOTA_SCOPE) ); const [maxSessions, setMaxSessions] = useState( typeof apiKey?.maxSessions === "number" && apiKey.maxSessions > 0 ? apiKey.maxSessions : 0 ); const [scheduleEnabled, setScheduleEnabled] = useState(apiKey?.accessSchedule?.enabled === true); const [scheduleFrom, setScheduleFrom] = useState(apiKey?.accessSchedule?.from ?? "08:00"); const [scheduleUntil, setScheduleUntil] = useState(apiKey?.accessSchedule?.until ?? "18:00"); const [scheduleDays, setScheduleDays] = useState( apiKey?.accessSchedule?.days ?? [1, 2, 3, 4, 5] ); const [scheduleTz, setScheduleTz] = useState( apiKey?.accessSchedule?.tz ?? Intl.DateTimeFormat().resolvedOptions().timeZone ); const [rateLimits, setRateLimits] = useState>( Array.isArray(apiKey?.rateLimits) ? apiKey.rateLimits : [] ); const [streamDefaultMode, setStreamDefaultMode] = useState( apiKey?.streamDefaultMode === "json" ? "json" : "legacy" ); const [nameError, setNameError] = useState(null); const [saveError, setSaveError] = useState(null); const [selectedConnections, setSelectedConnections] = useState(initialConnections); const [allowAllConnections, setAllowAllConnections] = useState(initialConnections.length === 0); const [expandedProviders, setExpandedProviders] = useState>(() => { // Expand all providers by default when in restrict mode with existing selections if (initialModels.length > 0) { return new Set(modelsByProvider.map(([p]) => p)); } return new Set(); }); const initialEndpoints = Array.isArray(apiKey?.allowedEndpoints) ? apiKey.allowedEndpoints : []; const [selectedEndpoints, setSelectedEndpoints] = useState(initialEndpoints); const [allowAllEndpoints, setAllowAllEndpoints] = useState(initialEndpoints.length === 0); const [disableNonPublicModels, setDisableNonPublicModels] = useState( apiKey?.disableNonPublicModels === true ); const [usageCommandEnabled, setUsageCommandEnabled] = useState( apiKey?.allowUsageCommand === true ); const [usageLimitEnabled, setUsageLimitEnabled] = useState(apiKey?.usageLimitEnabled === true); const [dailyUsageLimitUsd, setDailyUsageLimitUsd] = useState( typeof apiKey?.dailyUsageLimitUsd === "number" && apiKey.dailyUsageLimitUsd > 0 ? String(apiKey.dailyUsageLimitUsd) : "" ); const [weeklyUsageLimitUsd, setWeeklyUsageLimitUsd] = useState( typeof apiKey?.weeklyUsageLimitUsd === "number" && apiKey.weeklyUsageLimitUsd > 0 ? String(apiKey.weeklyUsageLimitUsd) : "" ); const getModelDisplayName = useCallback( (modelId: string) => modelId === CLAUDE_CODE_DEFAULT_MODEL_ID ? CLAUDE_CODE_DEFAULT_MODEL_NAME : modelId, [] ); // Memoize callbacks to prevent child re-renders const handleToggleModel = useCallback( (modelId: string) => { if (allowAll) return; setSelectedModels((prev) => { if (prev.includes(modelId)) { if (modelId === CLAUDE_CODE_DEFAULT_MODEL_ID) { setClaudeCodeFamiliesExpanded(false); } return prev.filter((m) => m !== modelId); } return [...prev, modelId]; }); }, [allowAll] ); const handleToggleProvider = useCallback( (provider: string, models: Model[]) => { if (allowAll) return; const modelIds = models.map((m) => m.id); setSelectedModels((prev) => { const allSelected = modelIds.every((id) => prev.includes(id)); if (allSelected) { return prev.filter((m) => !modelIds.includes(m)); } return [...new Set([...prev, ...modelIds])]; }); }, [allowAll] ); const handleSelectAll = useCallback(() => { setAllowAll(true); setSelectedModels([]); setBlockedClaudeCodeFamilies([]); setClaudeCodeFamiliesExpanded(false); }, []); const handleRestrictMode = useCallback(() => { setAllowAll(false); // Expand all providers when entering restrict mode const allProviders = new Set(modelsByProvider.map(([p]) => p)); setExpandedProviders(allProviders); }, [modelsByProvider]); const handleToggleExpand = useCallback((provider: string) => { setExpandedProviders((prev) => { const next = new Set(prev); if (next.has(provider)) { next.delete(provider); } else { next.add(provider); } return next; }); }, []); const handleSelectAllModels = useCallback(() => { const allModelIds = allModels.map((m) => m.id); setSelectedModels(allModelIds); setBlockedClaudeCodeFamilies([]); setClaudeCodeFamiliesExpanded(false); }, [allModels]); const handleDeselectAllModels = useCallback(() => { setSelectedModels([]); setBlockedClaudeCodeFamilies([]); setClaudeCodeFamiliesExpanded(false); }, []); const handleBlockClaudeCodeFamily = useCallback((familyId: ClaudeCodeBlockableFamilyId) => { setBlockedClaudeCodeFamilies((prev) => (prev.includes(familyId) ? prev : [...prev, familyId])); setSelectedModels((prev) => prev.filter((modelId) => !isClaudeCodeFamilyModel(modelId, familyId)) ); }, []); const handleToggleCombo = useCallback( (comboName: string) => { if (allowAllCombos) return; setSelectedCombos((prev) => prev.includes(comboName) ? prev.filter((name) => name !== comboName) : [...prev, comboName] ); }, [allowAllCombos] ); const handleToggleConnection = useCallback( (connectionId: string) => { if (allowAllConnections) return; setSelectedConnections((prev) => prev.includes(connectionId) ? prev.filter((c) => c !== connectionId) : [...prev, connectionId] ); }, [allowAllConnections] ); const handleToggleEndpoint = useCallback( (categoryId: string) => { if (allowAllEndpoints) return; setSelectedEndpoints((prev) => prev.includes(categoryId) ? prev.filter((e) => e !== categoryId) : [...prev, categoryId] ); }, [allowAllEndpoints] ); const parseUsdLimitInput = useCallback((value: string): number | null => { const parsed = Number(value); return Number.isFinite(parsed) && parsed > 0 ? parsed : null; }, []); const handleSave = useCallback(() => { // Clear previous inline errors setNameError(null); setSaveError(null); // Validate name inline before calling onSave const validation = validateKeyName(keyName, t); if (!validation.valid) { setNameError(validation.error || t("invalidKeyName")); return; } // Validate models selection if (!allowAll && !Array.isArray(selectedModels)) { setSaveError(t("invalidModelsSelection")); return; } // Limit number of selected models to prevent abuse if (!allowAll && selectedModels.length > MAX_SELECTED_MODELS) { setSaveError(t("cannotSelectMoreThanModels", { max: MAX_SELECTED_MODELS })); return; } const schedule: AccessSchedule | null = scheduleEnabled ? { enabled: true, from: scheduleFrom, until: scheduleUntil, days: scheduleDays, tz: scheduleTz, } : null; const hasClaudeCodeDefaultSelected = !allowAll && selectedModels.includes(CLAUDE_CODE_DEFAULT_MODEL_ID); const blockedModels = initialBlockedModels.filter( (pattern) => !CLAUDE_CODE_BLOCK_PATTERN_SET.has(pattern) ); if (hasClaudeCodeDefaultSelected) { for (const familyId of blockedClaudeCodeFamilies) { blockedModels.push(...CLAUDE_CODE_FAMILY_BLOCK_PATTERNS[familyId]); } } onSave( keyName, allowAll ? [] : selectedModels, allowAllCombos ? [] : selectedCombos, noLogEnabled, allowAllConnections ? [] : selectedConnections, autoResolveEnabled, keyIsActive, throttleDelayMs, keyIsBanned, expiresAt || null, maxSessions, schedule, rateLimits.length > 0 ? rateLimits : null, mergeApiKeyPermissionScopes(apiKey?.scopes, { manageEnabled, selfUsageEnabled, selfAccountQuotaEnabled, }), allowAllEndpoints ? [] : selectedEndpoints, streamDefaultMode, disableNonPublicModels, usageCommandEnabled, usageLimitEnabled, parseUsdLimitInput(dailyUsageLimitUsd), parseUsdLimitInput(weeklyUsageLimitUsd), blockedModels ); }, [ onSave, keyName, allowAll, selectedModels, allowAllCombos, selectedCombos, noLogEnabled, allowAllConnections, selectedConnections, autoResolveEnabled, keyIsActive, throttleDelayMs, keyIsBanned, expiresAt, maxSessions, manageEnabled, selfUsageEnabled, selfAccountQuotaEnabled, scheduleEnabled, scheduleFrom, scheduleUntil, scheduleDays, scheduleTz, rateLimits, allowAllEndpoints, selectedEndpoints, streamDefaultMode, disableNonPublicModels, usageCommandEnabled, usageLimitEnabled, dailyUsageLimitUsd, weeklyUsageLimitUsd, parseUsdLimitInput, blockedClaudeCodeFamilies, initialBlockedModels, apiKey?.scopes, t, ]); const selectedCount = selectedModels.length; const totalModels = allModels.length; const hasClaudeCodeDefaultSelected = !allowAll && selectedModels.includes(CLAUDE_CODE_DEFAULT_MODEL_ID); const orderedSelectedModels = useMemo(() => { if (!hasClaudeCodeDefaultSelected) return selectedModels; return [ CLAUDE_CODE_DEFAULT_MODEL_ID, ...selectedModels.filter((modelId) => modelId !== CLAUDE_CODE_DEFAULT_MODEL_ID), ]; }, [hasClaudeCodeDefaultSelected, selectedModels]); const visibleClaudeCodeFamilies = useMemo( () => CLAUDE_CODE_DEFAULT_FAMILIES.filter( (family) => family.id === "other" || !blockedClaudeCodeFamilies.includes(family.id as ClaudeCodeBlockableFamilyId) ), [blockedClaudeCodeFamilies] ); return (
{/* Key Name */}

{t("keyName")}

{t("keyNameDesc")}

{ setKeyName(e.target.value); setNameError(null); }} placeholder={t("keyNamePlaceholder")} maxLength={MAX_KEY_NAME_LENGTH} error={nameError} />
{/* Inline save error */} {saveError && (
error

{saveError}

)} {/* Access Mode Toggle */}
{/* Info Banner */}
{allowAll ? "info" : "warning"}

{allowAll ? t("allowAllDesc") : t("restrictDesc", { selectedCount, totalModels })}

{/* Key Active Toggle */}

{t("keyActive")}

{t("keyActiveDesc")}

{/* Max Sessions Limit (T08) */}

{t("maxActiveSessions")}

0 = unlimited. Return 429 when this key exceeds concurrent sticky sessions.

{ const parsed = Number.parseInt(e.target.value || "0", 10); setMaxSessions(Number.isFinite(parsed) && parsed > 0 ? parsed : 0); }} />
{/* Soft Throttle */}

Throttle Delay

Add a fixed delay before requests for this key are routed. 0 = no slowdown.

{ const parsed = Number.parseInt(e.target.value || "0", 10); setThrottleDelayMs( Number.isFinite(parsed) && parsed > 0 ? Math.min(parsed, 300000) : 0 ); }} />

milliseconds

{/* Custom Rate Limits */}

{t("apiManagerCustomRateLimits")}

{t("apiManagerCustomRateLimitsDesc")}

{rateLimits.length > 0 && (
{rateLimits.map((rl, index) => (
{ const val = parseInt(e.target.value) || 0; setRateLimits((prev) => { const next = [...prev]; next[index].limit = val; return next; }); }} placeholder={t("apiManagerRateLimitRequestsPlaceholder")} /> {t("apiManagerRateLimitReqPer")} { const val = parseInt(e.target.value) || 0; setRateLimits((prev) => { const next = [...prev]; next[index].window = val; return next; }); }} placeholder={t("apiManagerRateLimitSecondsPlaceholder")} /> sec
))}
)}
{/* Access Schedule */}

{t("accessSchedule")}

{t("accessScheduleDesc")}

{scheduleEnabled && (
setScheduleFrom(e.target.value)} className="w-full px-2 py-1.5 text-sm border border-border rounded-md bg-background text-text-main" />
setScheduleUntil(e.target.value)} className="w-full px-2 py-1.5 text-sm border border-border rounded-md bg-background text-text-main" />
{( [ [0, t("daySun")], [1, t("dayMon")], [2, t("dayTue")], [3, t("dayWed")], [4, t("dayThu")], [5, t("dayFri")], [6, t("daySat")], ] as [number, string][] ).map(([dayIdx, label]) => { const selected = scheduleDays.includes(dayIdx); return ( ); })}
setScheduleTz(e.target.value)} placeholder={t("apiManagerTimezonePlaceholder")} className="w-full px-2 py-1.5 text-sm border border-border rounded-md bg-background text-text-main font-mono" />

{t("scheduleTimezoneHint")}

)}
{/* Privacy Toggle */}

{t("noLogPayloadPrivacy")}

Disable request/response payload persistence for this API key.

{/* Auto-Resolve Toggle */}

{t("autoResolve")}

{t("autoResolveDesc")}

{/* Stream Default Compatibility */}

{t("streamDefaultMode")}

{t("streamDefaultModeDesc")}

{/* Ban Toggle (SECURITY) */}

{t("bannedStatus")}

Immediately revoke all access. Used for suspected abuse or compromised keys.

{/* Expiration Date */}

{t("expirationDate")}

Key will automatically stop working after this date.

{ const val = e.target.value; if (!val) { setExpiresAt(""); return; } const date = new Date(val); if (!Number.isNaN(date.getTime())) { setExpiresAt(date.toISOString()); } }} className="min-w-0 flex-1 px-2 py-1.5 text-sm border border-border rounded-md bg-background text-text-main" />
{/* Management Access */}

{t("managementAccess")}

{t("managementAccessDesc")}

{/* Self-service Visibility */}

{t("selfServiceVisibility")}

{t("selfServiceVisibilityDesc")}

{t("ownUsageVisibilityDesc")}

{t("sharedAccountQuotaVisibilityDesc")}

{t("localUsageCommandDesc")}

{/* Disable Non-Public Models Toggle */}

{t("disableNonPublicModels")}

{t("disableNonPublicModelsDesc")}

{/* Selected Models Summary (only in restrict mode) */} {!allowAll && selectedCount > 0 && (
{t("selectedCount", { count: selectedCount })}
{orderedSelectedModels.map((modelId) => { if (modelId === CLAUDE_CODE_DEFAULT_MODEL_ID) { return (
{claudeCodeFamiliesExpanded && (
)}
); } return ( {getModelDisplayName(modelId)} ); })}
)} {/* Search and Model Selection (only in restrict mode) */} {!allowAll && ( <>
onSearchChange(e.target.value)} placeholder={t("searchModels")} icon="search" /> {searchModel && ( )}
{modelsByProvider.length === 0 ? (
search_off

{t("noModelsFound")}

) : ( modelsByProvider.map(([provider, models]) => { const selectedInProvider = selectedModels.filter((m) => models.some((model) => model.id === m) ).length; const allSelected = models.every((m) => selectedModels.includes(m.id)); const someSelected = selectedInProvider > 0 && !allSelected; return (
{/* Expandable model list */} {expandedProviders.has(provider) && (
{models.map((model) => { const isSelected = selectedModels.includes(model.id); return ( ); })}
)}
); }) )}
)} {/* Allowed Connections Section */} {allConnections.length > 0 && (

{t("allowedConnections")}

{allowAllConnections ? "This key can use any active connection." : `Restricted to ${selectedConnections.length} connection${selectedConnections.length !== 1 ? "s" : ""}.`}

{!allowAllConnections && (
{Object.entries( allConnections.reduce>((acc, conn) => { const p = conn.provider || "Other"; if (!acc[p]) acc[p] = []; acc[p].push(conn); return acc; }, {}) ) .sort(([a], [b]) => compareTr(a, b)) .map(([provider, conns]) => (

{provider}

{conns.map((conn) => { const isSelected = selectedConnections.includes(conn.id); return ( ); })}
))}
)}
)} {/* Allowed Combos Section */} {allCombos.length > 0 && (

Allowed Combos

{allowAllCombos ? "This key can use any combo." : `Restricted to ${selectedCombos.length} combo${selectedCombos.length !== 1 ? "s" : ""}.`}

{!allowAllCombos && (
{allCombos .slice() .sort((a, b) => a.name.localeCompare(b.name)) .map((combo) => { const isSelected = selectedCombos.includes(combo.name); return ( ); })}
)}
)} {/* Allowed Endpoints Section */}

{t("endpointRestrictions")}

{allowAllEndpoints ? t("allEndpointsAllowed") : t("endpointsRestricted", { count: selectedEndpoints.length, })}

{!allowAllEndpoints && (
{ENDPOINT_CATEGORIES.map((cat) => { const isSelected = selectedEndpoints.includes(cat.id); return ( ); })}
)}
{/* Actions */}
); });