mirror of https://github.com/kortix-ai/suna.git
feat: add and delete new api key
This commit is contained in:
parent
74fc265187
commit
ea659c7a1e
|
@ -1,6 +1,6 @@
|
|||
"use client";
|
||||
|
||||
import { Eye, EyeOff } from "lucide-react";
|
||||
import { Eye, EyeOff, Plus, Trash } from "lucide-react";
|
||||
import { Button } from "../ui/button";
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../ui/card";
|
||||
import { isLocalMode } from "@/lib/config";
|
||||
|
@ -19,6 +19,8 @@ interface APIKeyForm {
|
|||
export function LocalEnvManager() {
|
||||
const queryClient = useQueryClient();
|
||||
const [visibleKeys, setVisibleKeys] = useState<Record<string, boolean>>({});
|
||||
const [newApiKeys, setNewApiKeys] = useState<{key: string, value: string, id: string}[]>([]);
|
||||
|
||||
const {data: apiKeys, isLoading} = useQuery({
|
||||
queryKey: ['api-keys'],
|
||||
queryFn: async() => {
|
||||
|
@ -33,8 +35,51 @@ export function LocalEnvManager() {
|
|||
});
|
||||
|
||||
const handleSave = async (data: APIKeyForm) => {
|
||||
updateApiKeys.mutate(data);
|
||||
const duplicate_key = newApiKeys.find(entry => data[entry.key.trim()]);
|
||||
if (duplicate_key) {
|
||||
toast.error(`Key ${duplicate_key.key} already exists`);
|
||||
return;
|
||||
}
|
||||
const submitData = {
|
||||
...data,
|
||||
...Object.fromEntries(newApiKeys.map(entry => [entry.key, entry.value]))
|
||||
}
|
||||
|
||||
updateApiKeys.mutate(submitData);
|
||||
}
|
||||
|
||||
const handleAddNewKey = () => {
|
||||
setNewApiKeys([...newApiKeys, {key: "", value: "", id: crypto.randomUUID()}]);
|
||||
}
|
||||
|
||||
const checkKeyIsDuplicate = (key: string) => {
|
||||
const trimmedKey = key.trim();
|
||||
const keyIsDuplicate =
|
||||
trimmedKey &&
|
||||
(
|
||||
(apiKeys && Object.keys(apiKeys).includes(trimmedKey)) ||
|
||||
newApiKeys.filter(e => e.key.trim() === trimmedKey).length > 1
|
||||
);
|
||||
return keyIsDuplicate;
|
||||
}
|
||||
|
||||
const handleNewKeyChange = (id: string, field: string, value: string) => {
|
||||
setNewApiKeys(prev =>
|
||||
prev.map(entry => entry.id === id ? {...entry, [field]: value} : entry)
|
||||
);
|
||||
}
|
||||
|
||||
const handleDeleteKey = (id: string) => {
|
||||
setNewApiKeys(prev => prev.filter(entry => entry.id !== id));
|
||||
}
|
||||
|
||||
const hasEmptyKeyValues = newApiKeys.some(entry => entry.key.trim() === "" || entry.value.trim() === "");
|
||||
const hasDuplicateKeys = (): boolean => {
|
||||
const allKeys = [...Object.keys(apiKeys || {}), ...newApiKeys.map(entry => entry.key.trim())];
|
||||
const uniqueKeys = new Set(allKeys);
|
||||
return uniqueKeys.size !== allKeys.length;
|
||||
}
|
||||
|
||||
const updateApiKeys = useMutation({
|
||||
mutationFn: async (data: APIKeyForm) => {
|
||||
const response = await backendApi.post('/env-vars', data);
|
||||
|
@ -43,6 +88,7 @@ export function LocalEnvManager() {
|
|||
},
|
||||
onSuccess: (data) => {
|
||||
toast.success(data.message);
|
||||
setNewApiKeys([]);
|
||||
},
|
||||
onError: () => {
|
||||
toast.error('Failed to update API keys');
|
||||
|
@ -120,11 +166,55 @@ export function LocalEnvManager() {
|
|||
|
||||
))}
|
||||
|
||||
<div className="flex justify-end">
|
||||
<div className="space-y-4">
|
||||
{newApiKeys.map(entry => {
|
||||
const keyIsDuplicate = checkKeyIsDuplicate(entry.key);
|
||||
return (
|
||||
|
||||
<div key={entry.id} className="space-y-2">
|
||||
<Label htmlFor={entry.id}>{entry.key || "New API Key"}</Label>
|
||||
<div className="space-x-2 flex">
|
||||
<Input
|
||||
id={`${entry.id}-key`}
|
||||
type="text"
|
||||
placeholder="KEY"
|
||||
value={entry.key}
|
||||
onChange={(e) => handleNewKeyChange(entry.id, 'key', e.target.value)}
|
||||
/>
|
||||
<Input
|
||||
id={`${entry.id}-value`}
|
||||
type="text"
|
||||
placeholder="VALUE"
|
||||
value={entry.value}
|
||||
onChange={(e) => handleNewKeyChange(entry.id, 'value', e.target.value)}
|
||||
/>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="icon"
|
||||
onClick={() => handleDeleteKey(entry.id)}
|
||||
>
|
||||
<Trash className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
{keyIsDuplicate && <p className="text-red-400 font-light">Key already exists</p>}
|
||||
</div>
|
||||
)})}
|
||||
</div>
|
||||
|
||||
<div className="flex justify-between">
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
onClick={handleAddNewKey}
|
||||
>
|
||||
<Plus className="h-4 w-4" />
|
||||
Add New Key
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="default"
|
||||
disabled={!isDirty}
|
||||
disabled={(!isDirty && newApiKeys.length === 0) || hasEmptyKeyValues || hasDuplicateKeys()}
|
||||
>Save</Button>
|
||||
</div>
|
||||
</form>
|
||||
|
|
Loading…
Reference in New Issue