Standardize API Key vs Virtual Key in UI

This commit is contained in:
yuneng-jiang
2025-12-01 13:13:39 -08:00
parent 7808a610f8
commit 21baa354cc
25 changed files with 99 additions and 120 deletions
@@ -38,10 +38,9 @@ const ModelSection = ({ modelName, metrics }: { modelName: string; metrics: Mode
</Card>
</Grid>
{/* Top API Keys Section */}
{metrics.top_api_keys && metrics.top_api_keys.length > 0 && (
<Card className="mt-4">
<Title>Top API Keys by Spend</Title>
<Title>Top Virtual Keys by Spend</Title>
<div className="mt-3">
<div className="grid grid-cols-1 gap-2">
{metrics.top_api_keys.map((keyData, index) => (
@@ -384,12 +383,12 @@ export const processActivityData = (
});
});
// Process API key breakdowns for each metric (skip if key is 'api_keys' to avoid duplication)
// Process Virtual Key breakdowns for each metric (skip if key is 'api_keys' to avoid duplication)
if (key !== "api_keys") {
Object.entries(modelMetrics).forEach(([model, _]) => {
const apiKeyBreakdown: Record<string, TopApiKeyData> = {};
// Aggregate API key data across all days
// Aggregate Virtual Key data across all days
dailyActivity.results.forEach((day) => {
const modelData = day.breakdown[key]?.[model];
if (modelData && "api_key_breakdown" in modelData) {
@@ -569,7 +569,7 @@ const BulkCreateUsersButton: React.FC<BulkCreateUsersProps> = ({
<li>Download our CSV template</li>
<li>Add your users&apos; information to the spreadsheet</li>
<li>Save the file and upload it here</li>
<li>After creation, download the results file containing the API keys for each user</li>
<li>After creation, download the results file containing the Virtual Keys for each user</li>
</ol>
<div className="bg-gray-50 p-4 rounded-md border border-gray-200 mb-4">
@@ -809,9 +809,9 @@ const BulkCreateUsersButton: React.FC<BulkCreateUsersProps> = ({
<div>
<Text className="font-medium text-blue-800">User creation complete</Text>
<Text className="block text-sm text-blue-700 mt-1">
<span className="font-medium">Next step:</span> Download the credentials file containing API
keys and invitation links. Users will need these API keys to make LLM requests through
LiteLLM.
<span className="font-medium">Next step:</span> Download the credentials file containing
Virtual Keys and invitation links. Users will need these Virtual Keys to make LLM requests
through LiteLLM.
</Text>
</div>
</div>
@@ -293,7 +293,11 @@ const CacheDashboard: React.FC<CachePageProps> = ({ accessToken, token, userRole
<Card>
<Grid numItems={3} className="gap-4 mt-4">
<Col>
<MultiSelect placeholder="Select API Keys" value={selectedApiKeys} onValueChange={setSelectedApiKeys}>
<MultiSelect
placeholder="Select Virtual Keys"
value={selectedApiKeys}
onValueChange={setSelectedApiKeys}
>
{uniqueApiKeys.map((key) => (
<MultiSelectItem key={key} value={key}>
{key}
@@ -388,11 +392,7 @@ const CacheDashboard: React.FC<CachePageProps> = ({ accessToken, token, userRole
/>
</TabPanel>
<TabPanel>
<CacheSettings
accessToken={accessToken}
userRole={userRole}
userID={userID}
/>
<CacheSettings accessToken={accessToken} userRole={userRole} userID={userID} />
</TabPanel>
</TabPanels>
</TabGroup>
@@ -21,7 +21,7 @@ const PassThroughSecuritySection: React.FC<PassThroughSecuritySectionProps> = ({
<Card className="p-6">
<Title className="text-lg font-semibold text-gray-900 mb-2">Security</Title>
<Subtitle className="text-gray-600 mb-4">
When enabled, requests to this endpoint will require a valid LiteLLM API key
When enabled, requests to this endpoint will require a valid LiteLLM Virtual Key
</Subtitle>
{premiumUser ? (
<Form.Item name="auth" valuePropName="checked" className="mb-0">
@@ -35,22 +35,13 @@ const PassThroughSecuritySection: React.FC<PassThroughSecuritySectionProps> = ({
) : (
<div>
<div className="flex items-center mb-3">
<Switch
disabled
checked={false}
style={{ outline: '2px solid #d1d5db', outlineOffset: '2px' }}
/>
<Switch disabled checked={false} style={{ outline: "2px solid #d1d5db", outlineOffset: "2px" }} />
<span className="ml-2 text-sm text-gray-400">Authentication (Premium)</span>
</div>
<div className="p-3 bg-yellow-50 border border-yellow-200 rounded-lg">
<Text className="text-sm text-yellow-800">
Setting authentication for pass-through endpoints is a LiteLLM Enterprise feature. Get a trial key{" "}
<a
href="https://www.litellm.ai/#pricing"
target="_blank"
rel="noopener noreferrer"
className="underline"
>
<a href="https://www.litellm.ai/#pricing" target="_blank" rel="noopener noreferrer" className="underline">
here
</a>
.
@@ -63,4 +54,3 @@ const PassThroughSecuritySection: React.FC<PassThroughSecuritySectionProps> = ({
};
export default PassThroughSecuritySection;
@@ -69,7 +69,8 @@ const DashboardTeam: React.FC<DashboardTeamProps> = ({
<Title>Select Team</Title>
<Text>
If you belong to multiple teams, this setting controls which team is used by default when creating new API Keys.
If you belong to multiple teams, this setting controls which team is used by default when creating new Virtual
Keys.
</Text>
<Text className="mt-3 mb-3">
<b>Default Team:</b> If no team_id is set for a key, it will be grouped under here.
@@ -550,7 +550,7 @@ const EntityUsage: React.FC<EntityUsageProps> = ({
{/* Top API Keys */}
<Col numColSpan={1}>
<Card>
<Title>Top API Keys</Title>
<Title>Top Virtual Keys</Title>
<TopKeyView
topKeys={getTopAPIKeys()}
accessToken={accessToken}
@@ -90,7 +90,7 @@ const MakeAgentPublicForm: React.FC<MakeAgentPublicFormProps> = ({
setLoading(true);
try {
const agentIdsToMakePublic = Array.from(selectedAgents);
// Make batch API call for all agents
await makeAgentsPublicCall(accessToken, agentIdsToMakePublic);
@@ -127,8 +127,8 @@ const MakeAgentPublicForm: React.FC<MakeAgentPublicFormProps> = ({
</div>
<Text className="text-sm text-gray-600">
Select the agents you want to be visible on the public model hub. Users will still require a valid API key to
use these agents.
Select the agents you want to be visible on the public model hub. Users will still require a valid Virtual Key
to use these agents.
</Text>
<div className="max-h-96 overflow-y-auto border rounded-lg p-4">
@@ -141,10 +141,7 @@ const MakeAgentPublicForm: React.FC<MakeAgentPublicFormProps> = ({
agentHubData.map((agent) => {
const agentId = agent.agent_id || agent.name;
return (
<div
key={agentId}
className="flex items-center space-x-3 p-3 border rounded-lg hover:bg-gray-50"
>
<div key={agentId} className="flex items-center space-x-3 p-3 border rounded-lg hover:bg-gray-50">
<Checkbox
checked={selectedAgents.has(agentId)}
onChange={(e) => handleAgentSelection(agentId, e.target.checked)}
@@ -217,9 +214,7 @@ const MakeAgentPublicForm: React.FC<MakeAgentPublicFormProps> = ({
</Badge>
)}
</div>
{agent?.description && (
<Text className="text-xs text-gray-600 mt-1">{agent.description}</Text>
)}
{agent?.description && <Text className="text-xs text-gray-600 mt-1">{agent.description}</Text>}
</div>
</div>
);
@@ -296,4 +291,3 @@ const MakeAgentPublicForm: React.FC<MakeAgentPublicFormProps> = ({
};
export default MakeAgentPublicForm;
@@ -76,7 +76,7 @@ const MakeMCPPublicForm: React.FC<MakeMCPPublicFormProps> = ({
const publicServerIds = mcpHubData
.filter((server) => server.mcp_info?.is_public === true)
.map((server) => server.server_id);
// Preselect servers that are already public
setSelectedServers(new Set(publicServerIds));
}
@@ -91,7 +91,7 @@ const MakeMCPPublicForm: React.FC<MakeMCPPublicFormProps> = ({
setLoading(true);
try {
const serverIdsToMakePublic = Array.from(selectedServers);
// Make batch API call for all servers
await makeMCPPublicCall(accessToken, serverIdsToMakePublic);
@@ -128,8 +128,8 @@ const MakeMCPPublicForm: React.FC<MakeMCPPublicFormProps> = ({
</div>
<Text className="text-sm text-gray-600">
Select the MCP servers you want to be visible on the public model hub. Users will still require a valid API key to
use these servers.
Select the MCP servers you want to be visible on the public model hub. Users will still require a valid
Virtual Key to use these servers.
</Text>
<div className="max-h-96 overflow-y-auto border rounded-lg p-4">
@@ -161,22 +161,20 @@ const MakeMCPPublicForm: React.FC<MakeMCPPublicFormProps> = ({
<Badge color="blue" size="sm">
{server.transport}
</Badge>
<Badge
<Badge
color={
server.status === "active" || server.status === "healthy"
? "green"
server.status === "active" || server.status === "healthy"
? "green"
: server.status === "inactive" || server.status === "unhealthy"
? "red"
: "gray"
}
? "red"
: "gray"
}
size="sm"
>
{server.status || "unknown"}
</Badge>
</div>
<Text className="text-xs text-gray-600 mt-1">
{server.description || server.url}
</Text>
<Text className="text-xs text-gray-600 mt-1">{server.description || server.url}</Text>
{server.allowed_tools && server.allowed_tools.length > 0 && (
<div className="flex flex-wrap gap-1 mt-1">
{server.allowed_tools.slice(0, 3).map((tool, idx) => (
@@ -236,14 +234,14 @@ const MakeMCPPublicForm: React.FC<MakeMCPPublicFormProps> = ({
<Badge color="blue" size="xs">
{server.transport}
</Badge>
<Badge
<Badge
color={
server.status === "active" || server.status === "healthy"
? "green"
server.status === "active" || server.status === "healthy"
? "green"
: server.status === "inactive" || server.status === "unhealthy"
? "red"
: "gray"
}
? "red"
: "gray"
}
size="xs"
>
{server.status || "unknown"}
@@ -251,12 +249,8 @@ const MakeMCPPublicForm: React.FC<MakeMCPPublicFormProps> = ({
</>
)}
</div>
{server?.description && (
<Text className="text-xs text-gray-600 mt-1">{server.description}</Text>
)}
{server?.url && (
<Text className="text-xs text-gray-500 mt-1">{server.url}</Text>
)}
{server?.description && <Text className="text-xs text-gray-600 mt-1">{server.description}</Text>}
{server?.url && <Text className="text-xs text-gray-500 mt-1">{server.url}</Text>}
</div>
</div>
);
@@ -267,8 +261,8 @@ const MakeMCPPublicForm: React.FC<MakeMCPPublicFormProps> = ({
<div className="bg-blue-50 border border-blue-200 rounded-lg p-3">
<Text className="text-sm text-blue-800">
Total: <strong>{selectedServers.size}</strong> MCP server{selectedServers.size !== 1 ? "s" : ""} will be made
public
Total: <strong>{selectedServers.size}</strong> MCP server{selectedServers.size !== 1 ? "s" : ""} will be
made public
</Text>
</div>
</div>
@@ -333,4 +327,3 @@ const MakeMCPPublicForm: React.FC<MakeMCPPublicFormProps> = ({
};
export default MakeMCPPublicForm;
@@ -152,8 +152,8 @@ const MakeModelPublicForm: React.FC<MakeModelPublicFormProps> = ({
</div>
<Text className="text-sm text-gray-600">
Select the models you want to be visible on the public model hub. Users will still require a valid API key to
use these models.
Select the models you want to be visible on the public model hub. Users will still require a valid Virtual Key
to use these models.
</Text>
{/* Filters */}
@@ -220,12 +220,12 @@ const MCPConnect: React.FC<MCPConnectProps> = ({ currentServerAccessGroups = []
<Space direction="vertical" size="large" className="w-full">
<FeatureCard
icon={<KeyIcon className="text-emerald-600" size={16} />}
title="API Key Setup"
description="Configure your LiteLLM Proxy API key for authentication"
title="Virtual Key Setup"
description="Configure your LiteLLM Proxy Virtual Key for authentication"
>
<Space direction="vertical" size="middle" className="w-full">
<div>
<Text>Get your API key from your LiteLLM Proxy dashboard or contact your administrator</Text>
<Text>Get your Virtual Key from your LiteLLM Proxy dashboard or contact your administrator</Text>
</div>
<CodeBlock title="Environment Variable" code='export LITELLM_API_KEY="sk-..."' copyKey="litellm-env" />
</Space>
@@ -249,7 +249,7 @@ const MCPConnect: React.FC<MCPConnectProps> = ({ currentServerAccessGroups = []
<CodeBlock
code={`curl --location '${proxyBaseUrl}/v1/responses' \\
--header 'Content-Type: application/json' \\
--header "Authorization: Bearer $LITELLM_API_KEY" \\
--header "Authorization: Bearer $LITELLM_VIRTUAL_KEY" \\
--data '{
"model": "gpt-4",
"tools": [
@@ -259,7 +259,7 @@ const MCPConnect: React.FC<MCPConnectProps> = ({ currentServerAccessGroups = []
"server_url": "${proxyBaseUrl}/mcp",
"require_approval": "never",
"headers": {
"x-litellm-api-key": "Bearer YOUR_LITELLM_API_KEY",
"x-litellm-api-key": "Bearer YOUR_LITELLM_VIRTUAL_KEY",
"x-mcp-servers": ["Zapier_MCP,dev"]
}
}
@@ -239,7 +239,7 @@ describe("NewUsage", () => {
// Check for chart titles
expect(screen.getByText("Daily Spend")).toBeInTheDocument();
expect(screen.getByText("Top API Keys")).toBeInTheDocument();
expect(screen.getByText("Top Virtual Keys")).toBeInTheDocument();
});
it("should switch between tabs correctly", async () => {
@@ -580,7 +580,7 @@ const NewUsagePage: React.FC<NewUsagePageProps> = ({
{/* Top API Keys */}
<Col numColSpan={1}>
<Card className="h-full">
<Title>Top API Keys</Title>
<Title>Top Virtual Keys</Title>
<TopKeyView
topKeys={getTopKeys()}
accessToken={accessToken}
@@ -405,7 +405,7 @@ const CreateKey: React.FC<CreateKeyProps> = ({
setApiKey(response["key"]);
setSoftBudget(response["soft_budget"]);
NotificationsManager.success("API Key Created");
NotificationsManager.success("Virtual Key Created");
form.resetFields();
localStorage.removeItem("userData" + userID);
} catch (error) {
@@ -415,7 +415,7 @@ const CreateKey: React.FC<CreateKeyProps> = ({
};
const handleCopy = () => {
NotificationsManager.success("API Key copied to clipboard");
NotificationsManager.success("Virtual Key copied to clipboard");
};
useEffect(() => {
@@ -505,7 +505,7 @@ const CreateKey: React.FC<CreateKeyProps> = ({
label={
<span>
Owned By{" "}
<Tooltip title="Select who will own this API key">
<Tooltip title="Select who will own this Virtual Key">
<InfoCircleOutlined style={{ marginLeft: "4px" }} />
</Tooltip>
</span>
@@ -594,8 +594,8 @@ const CreateKey: React.FC<CreateKeyProps> = ({
{isFormDisabled && (
<div className="mb-8 p-4 bg-blue-50 border border-blue-200 rounded-md">
<Text className="text-blue-800 text-sm">
Please select a team to continue configuring your API key. If you do not see any teams, please contact
your Proxy Admin to either provide you with access to models or to add you to a team.
Please select a team to continue configuring your Virtual Key. If you do not see any teams, please
contact your Proxy Admin to either provide you with access to models or to add you to a team.
</Text>
</div>
)}
@@ -1277,7 +1277,7 @@ const CreateKey: React.FC<CreateKeyProps> = ({
<Col numColSpan={1}>
{apiKey != null ? (
<div>
<Text className="mt-3">API Key:</Text>
<Text className="mt-3">Virtual Key:</Text>
<div
style={{
background: "#f8f8f8",
@@ -1290,7 +1290,7 @@ const CreateKey: React.FC<CreateKeyProps> = ({
</div>
<CopyToClipboard text={apiKey} onCopy={handleCopy}>
<Button className="mt-3">Copy API Key</Button>
<Button className="mt-3">Copy Virtual Key</Button>
</CopyToClipboard>
{/* <Button className="mt-3" onClick={sendSlackAlert}>
Test Key
@@ -113,7 +113,7 @@ export function RegenerateKeyModal({
formValues,
);
setRegeneratedKey(response.key);
NotificationManager.success("API Key regenerated successfully");
NotificationManager.success("Virtual Key regenerated successfully");
console.log("Full regenerate response:", response); // Debug log to see what's returned
@@ -164,7 +164,7 @@ export function RegenerateKeyModal({
return (
<Modal
title="Regenerate API Key"
title="Regenerate Virtual Key"
open={visible}
onCancel={handleClose}
footer={
@@ -199,15 +199,15 @@ export function RegenerateKeyModal({
<div className="bg-gray-100 p-2 rounded mb-2">
<pre className="break-words whitespace-normal">{selectedToken?.key_alias || "No alias set"}</pre>
</div>
<Text className="mt-3">New API Key:</Text>
<Text className="mt-3">New Virtual Key:</Text>
<div className="bg-gray-100 p-2 rounded mb-2">
<pre className="break-words whitespace-normal">{regeneratedKey}</pre>
</div>
<CopyToClipboard
text={regeneratedKey}
onCopy={() => NotificationManager.success("API Key copied to clipboard")}
onCopy={() => NotificationManager.success("Virtual Key copied to clipboard")}
>
<Button className="mt-3">Copy API Key</Button>
<Button className="mt-3">Copy Virtual Key</Button>
</CopyToClipboard>
</Col>
</Grid>
@@ -691,7 +691,7 @@ const ChatUI: React.FC<ChatUIProps> = ({
const effectiveApiKey = apiKeySource === "session" ? accessToken : apiKey;
if (!effectiveApiKey) {
NotificationsManager.fromBackend("Please provide an API key or select Current UI Session");
NotificationsManager.fromBackend("Please provide a Virtual Key or select Current UI Session");
return;
}
@@ -1003,7 +1003,7 @@ const ChatUI: React.FC<ChatUIProps> = ({
<div className="space-y-4">
<div>
<Text className="font-medium block mb-2 text-gray-700 flex items-center">
<KeyOutlined className="mr-2" /> API Key Source
<KeyOutlined className="mr-2" /> Virtual Key Source
</Text>
<Select
disabled={disabledPersonalKeyCreation}
@@ -1021,7 +1021,7 @@ const ChatUI: React.FC<ChatUIProps> = ({
{apiKeySource === "custom" && (
<TextInput
className="mt-2"
placeholder="Enter custom API key"
placeholder="Enter custom Virtual Key"
type="password"
onValueChange={setApiKey}
value={apiKey}
@@ -395,7 +395,7 @@ export default function CompareUI({ accessToken, disabledPersonalKeyCreation }:
return;
}
if (!effectiveApiKey) {
NotificationsManager.fromBackend("Please provide an API key or select Current UI Session");
NotificationsManager.fromBackend("Please provide a Virtual Key or select Current UI Session");
return;
}
const targetComparisons = comparisons;
@@ -551,7 +551,7 @@ export default function CompareUI({ accessToken, disabledPersonalKeyCreation }:
<div className="border-b px-4 py-2">
<div className="flex flex-wrap items-center justify-between gap-3">
<div className="flex items-center gap-2">
<span className="text-sm font-medium text-gray-600">API Key Source</span>
<span className="text-sm font-medium text-gray-600">Virtual Key Source</span>
<Select
value={apiKeySource}
onChange={(value) => setApiKeySource(value as "session" | "custom")}
@@ -567,7 +567,7 @@ export default function CompareUI({ accessToken, disabledPersonalKeyCreation }:
<Input.Password
value={customApiKey}
onChange={(event) => setCustomApiKey(event.target.value)}
placeholder="Enter API key"
placeholder="Enter Virtual Key"
className="w-56"
/>
)}
@@ -20,7 +20,7 @@ export async function makeAnthropicMessagesRequest(
selectedMCPTools?: string[],
) {
if (!accessToken) {
throw new Error("API key is required");
throw new Error("Virtual Key is required");
}
const isLocal = process.env.NODE_ENV === "development";
@@ -9,7 +9,7 @@ export async function makeOpenAIEmbeddingsRequest(
tags?: string[],
) {
if (!accessToken) {
throw new Error("API key is required");
throw new Error("Virtual Key is required");
}
// Base URL should be the current base_url
@@ -24,7 +24,7 @@ export async function makeOpenAIResponsesRequest(
onMCPEvent?: (event: MCPEvent) => void,
) {
if (!accessToken) {
throw new Error("API key is required");
throw new Error("Virtual Key is required");
}
// Base URL should be the current base_url
@@ -245,7 +245,7 @@ import KeyInfoView from "./key_info_view";
const baseKeyData = {
token_id: "tok_123",
token: "tok_123",
key_alias: "My API Key",
key_alias: "My Virtual Key",
key_name: "sk-xxxx",
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
@@ -297,7 +297,7 @@ export default function KeyInfoView({
<Button icon={ArrowLeftIcon} variant="light" onClick={onClose} className="mb-4">
{backButtonText}
</Button>
<Title>{currentKeyData.key_alias || "API Key"}</Title>
<Title>{currentKeyData.key_alias || "Virtual Key"}</Title>
<div className="flex items-center cursor-pointer mb-2 space-y-6">
<div>
@@ -381,7 +381,7 @@ export default function KeyInfoView({
{/* Delete Confirmation Modal */}
{isDeleteModalOpen &&
(() => {
const keyName = currentKeyData?.key_alias || currentKeyData?.token_id || "API Key";
const keyName = currentKeyData?.key_alias || currentKeyData?.token_id || "Virtual Key";
const isValid = deleteConfirmInput === keyName;
return (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
@@ -415,7 +415,7 @@ export default function KeyInfoView({
</div>
<div>
<p className="text-base font-medium text-red-600">
Warning: You are about to delete this API key.
Warning: You are about to delete this Virtual Key.
</p>
<p className="text-base text-red-600 mt-2">
This action is irreversible and will immediately revoke access for any applications using this
@@ -423,7 +423,7 @@ export default function KeyInfoView({
</p>
</div>
</div>
<p className="text-base text-gray-600 mb-5">Are you sure you want to delete this API key?</p>
<p className="text-base text-gray-600 mb-5">Are you sure you want to delete this Virtual Key?</p>
<div className="mb-5">
<label className="block text-base font-medium text-gray-700 mb-2">
{`Type `}
@@ -273,7 +273,7 @@ const ViewKeyTable: React.FC<ViewKeyTableProps> = ({
const handleRegenerateKey = async () => {
if (!premiumUser) {
NotificationManager.warning({
description: "Regenerate API Key is an Enterprise feature. Please upgrade to use this feature.",
description: "Regenerate Virtual Key is an Enterprise feature. Please upgrade to use this feature.",
});
return;
}
@@ -298,10 +298,10 @@ const ViewKeyTable: React.FC<ViewKeyTableProps> = ({
setRegenerateDialogVisible(false);
regenerateForm.resetFields();
NotificationManager.success({ description: "API Key regenerated successfully" });
NotificationManager.success({ description: "Virtual Key regenerated successfully" });
} catch (error) {
console.error("Error regenerating key:", error);
NotificationManager.error({ description: "Failed to regenerate API Key" });
NotificationManager.error({ description: "Failed to regenerate Virtual Key" });
}
};
@@ -366,7 +366,7 @@ const ViewKeyTable: React.FC<ViewKeyTableProps> = ({
</div>
<div>
<p className="text-base font-medium text-red-600">
Warning: You are about to delete this API key.
Warning: You are about to delete this Virtual Key.
</p>
<p className="text-base text-red-600 mt-2">
This action is irreversible and will immediately revoke access for any applications using this
@@ -374,7 +374,7 @@ const ViewKeyTable: React.FC<ViewKeyTableProps> = ({
</p>
</div>
</div>
<p className="text-base text-gray-600 mb-5">Are you sure you want to delete this API key?</p>
<p className="text-base text-gray-600 mb-5">Are you sure you want to delete this Virtual Key?</p>
<div className="mb-5">
<label className="block text-base font-medium text-gray-700 mb-2">
{`Type `}
@@ -407,7 +407,7 @@ const ViewKeyTable: React.FC<ViewKeyTableProps> = ({
disabled={!isValid}
className={`px-5 py-3 rounded-md text-base font-medium text-white focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 ${isValid ? "bg-red-600 hover:bg-red-700" : "bg-red-300 cursor-not-allowed"}`}
>
Delete Key
Delete Virtual Key
</button>
</div>
</div>
@@ -417,7 +417,7 @@ const ViewKeyTable: React.FC<ViewKeyTableProps> = ({
{/* Regenerate Key Form Modal */}
<Modal
title="Regenerate API Key"
title="Regenerate Virtual Key"
visible={regenerateDialogVisible}
onCancel={() => {
setRegenerateDialogVisible(false);
@@ -516,7 +516,7 @@ const ViewKeyTable: React.FC<ViewKeyTableProps> = ({
{selectedToken?.key_alias || "No alias set"}
</pre>
</div>
<Text className="mt-3">New API Key:</Text>
<Text className="mt-3">New Virtual Key:</Text>
<div
style={{
background: "#f8f8f8",
@@ -529,9 +529,9 @@ const ViewKeyTable: React.FC<ViewKeyTableProps> = ({
</div>
<CopyToClipboard
text={regeneratedKey}
onCopy={() => NotificationManager.success({ description: "API Key copied to clipboard" })}
onCopy={() => NotificationManager.success({ description: "Virtual Key copied to clipboard" })}
>
<Button className="mt-3">Copy API Key</Button>
<Button className="mt-3">Copy Virtual Key</Button>
</CopyToClipboard>
</Col>
</Grid>
@@ -615,7 +615,7 @@ const UsagePage: React.FC<UsagePageProps> = ({ accessToken, token, userRole, use
</Col>
<Col numColSpan={1}>
<Card className="h-full">
<Title>Top API Keys</Title>
<Title>Top Virtual Keys</Title>
<TopKeyView
topKeys={topKeys}
accessToken={accessToken}
@@ -78,14 +78,14 @@ export const columns = (
),
},
{
header: "API Keys",
header: "Virtual Keys",
accessorKey: "key_count",
enableSorting: false,
cell: ({ row }) => (
<Grid numItems={2}>
{row.original.key_count > 0 ? (
<Badge size="xs" color="indigo">
{row.original.key_count} Keys
{row.original.key_count} {row.original.key_count === 1 ? "Key" : "Keys"}
</Badge>
) : (
<Badge size="xs" color="gray">
@@ -320,9 +320,11 @@ export default function UserInfoView({
</Card>
<Card>
<Text>API Keys</Text>
<Text>Virtual Keys</Text>
<div className="mt-2">
<Text>{userData.keys?.length || 0} keys</Text>
<Text>
{userData.keys?.length || 0} {userData.keys?.length === 1 ? "Key" : "Keys"}
</Text>
</div>
</Card>
@@ -467,7 +469,7 @@ export default function UserInfoView({
</div>
<div>
<Text className="font-medium">API Keys</Text>
<Text className="font-medium">Virtual Keys</Text>
<div className="flex flex-wrap gap-2 mt-1">
{userData.keys?.length && userData.keys?.length > 0 ? (
userData.keys.map((key, index) => (
@@ -476,7 +478,7 @@ export default function UserInfoView({
</span>
))
) : (
<Text>No API keys</Text>
<Text>No Virtual Keys</Text>
)}
</div>
</div>