working regenerate key flow

This commit is contained in:
Ishaan Jaff 2024-08-26 18:28:26 -07:00
parent 2615edc468
commit 40c018272c
2 changed files with 112 additions and 2 deletions

View file

@ -770,6 +770,37 @@ export const claimOnboardingToken = async (
throw error; throw error;
} }
}; };
export const regenerateKeyCall = async (accessToken: string, keyToRegenerate: string) => {
try {
const url = proxyBaseUrl
? `${proxyBaseUrl}/key/${keyToRegenerate}/regenerate`
: `/key/${keyToRegenerate}/regenerate`;
const response = await fetch(url, {
method: "POST",
headers: {
[globalLitellmHeaderName]: `Bearer ${accessToken}`,
"Content-Type": "application/json",
},
body: JSON.stringify({}),
});
if (!response.ok) {
const errorData = await response.text();
handleError(errorData);
throw new Error("Network response was not ok");
}
const data = await response.json();
console.log("Regenerate key Response:", data);
return data;
} catch (error) {
console.error("Failed to regenerate key:", error);
throw error;
}
};
let ModelListerrorShown = false; let ModelListerrorShown = false;
let errorTimer: NodeJS.Timeout | null = null; let errorTimer: NodeJS.Timeout | null = null;

View file

@ -1,12 +1,14 @@
"use client"; "use client";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { keyDeleteCall, modelAvailableCall } from "./networking"; import { keyDeleteCall, modelAvailableCall } from "./networking";
import { InformationCircleIcon, StatusOnlineIcon, TrashIcon, PencilAltIcon } from "@heroicons/react/outline"; import { InformationCircleIcon, StatusOnlineIcon, TrashIcon, PencilAltIcon, RefreshIcon } from "@heroicons/react/outline";
import { keySpendLogsCall, PredictedSpendLogsCall, keyUpdateCall, modelInfoCall } from "./networking"; import { keySpendLogsCall, PredictedSpendLogsCall, keyUpdateCall, modelInfoCall, regenerateKeyCall } from "./networking";
import { import {
Badge, Badge,
Card, Card,
Table, Table,
Grid,
Col,
Button, Button,
TableBody, TableBody,
TableCell, TableCell,
@ -33,6 +35,8 @@ import {
Select, Select,
} from "antd"; } from "antd";
import { CopyToClipboard } from "react-copy-to-clipboard";
const { Option } = Select; const { Option } = Select;
const isLocal = process.env.NODE_ENV === "development"; const isLocal = process.env.NODE_ENV === "development";
const proxyBaseUrl = isLocal ? "http://localhost:4000" : null; const proxyBaseUrl = isLocal ? "http://localhost:4000" : null;
@ -109,6 +113,8 @@ const ViewKeyTable: React.FC<ViewKeyTableProps> = ({
const [userModels, setUserModels] = useState([]); const [userModels, setUserModels] = useState([]);
const initialKnownTeamIDs: Set<string> = new Set(); const initialKnownTeamIDs: Set<string> = new Set();
const [modelLimitModalVisible, setModelLimitModalVisible] = useState(false); const [modelLimitModalVisible, setModelLimitModalVisible] = useState(false);
const [regenerateDialogVisible, setRegenerateDialogVisible] = useState(false);
const [regeneratedKey, setRegeneratedKey] = useState<string | null>(null);
const [knownTeamIDs, setKnownTeamIDs] = useState(initialKnownTeamIDs); const [knownTeamIDs, setKnownTeamIDs] = useState(initialKnownTeamIDs);
@ -612,6 +618,18 @@ const ViewKeyTable: React.FC<ViewKeyTableProps> = ({
setKeyToDelete(null); setKeyToDelete(null);
}; };
const handleRegenerateKey = async () => {
try {
const response = await regenerateKeyCall(accessToken, selectedToken.token);
setRegeneratedKey(response.key);
setRegenerateDialogVisible(false);
message.success("API Key regenerated successfully");
} catch (error) {
console.error("Error regenerating key:", error);
message.error("Failed to regenerate API Key");
}
};
if (data == null) { if (data == null) {
return; return;
} }
@ -768,6 +786,7 @@ const ViewKeyTable: React.FC<ViewKeyTableProps> = ({
size="sm" size="sm"
/> />
<Modal <Modal
open={infoDialogVisible} open={infoDialogVisible}
@ -867,6 +886,14 @@ const ViewKeyTable: React.FC<ViewKeyTableProps> = ({
size="sm" size="sm"
onClick={() => handleEditClick(item)} onClick={() => handleEditClick(item)}
/> />
<Icon
onClick={() => {
setSelectedToken(item);
setRegenerateDialogVisible(true);
}}
icon={RefreshIcon}
size="sm"
/>
<Icon <Icon
onClick={() => handleDelete(item)} onClick={() => handleDelete(item)}
icon={TrashIcon} icon={TrashIcon}
@ -942,6 +969,58 @@ const ViewKeyTable: React.FC<ViewKeyTableProps> = ({
accessToken={accessToken} accessToken={accessToken}
/> />
)} )}
{/* Regenerate Key Confirmation Dialog */}
<Modal
title="Regenerate API Key"
visible={regenerateDialogVisible}
onOk={handleRegenerateKey}
onCancel={() => setRegenerateDialogVisible(false)}
>
<p>Are you sure you want to regenerate this key?</p>
<p>Key Alias:</p>
<pre>{selectedToken?.key_alias || 'No alias set'}</pre>
</Modal>
{/* Regenerated Key Display Modal */}
{regeneratedKey && (
<Modal
visible={!!regeneratedKey}
onOk={() => setRegeneratedKey(null)}
onCancel={() => setRegeneratedKey(null)}
footer={null}
>
<Grid numItems={1} className="gap-2 w-full">
<Title>Save your New Key</Title>
<Col numColSpan={1}>
<p>
Please save this new secret key somewhere safe and accessible. For
security reasons, <b>you will not be able to view it again</b> through
your LiteLLM account. If you lose this secret key, you will need to
generate a new one.
</p>
</Col>
<Col numColSpan={1}>
<Text className="mt-3">New API Key:</Text>
<div
style={{
background: "#f8f8f8",
padding: "10px",
borderRadius: "5px",
marginBottom: "10px",
}}
>
<pre style={{ wordWrap: "break-word", whiteSpace: "normal" }}>
{regeneratedKey}
</pre>
</div>
<CopyToClipboard text={regeneratedKey} onCopy={() => message.success("API Key copied to clipboard")}>
<Button className="mt-3">Copy API Key</Button>
</CopyToClipboard>
</Col>
</Grid>
</Modal>
)}
</div> </div>
); );
}; };