pablonyx df67ca18d8
My docs cleanup (#4519)
* update

* improved my docs

* nit

* nit

* k

* push changes

* update

* looking good

* k

* fix preprocessing

* try a fix

* k

* update

* nit

* k

* quick nits

* Cleanup / fixes

* Fixes

* Fix build

* fix

* fix quality checks

---------

Co-authored-by: Weves <chrisweaver101@gmail.com>
2025-04-25 05:20:33 +00:00

279 lines
8.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import { ThreeDotsLoader } from "@/components/Loading";
import { AdminPageTitle } from "@/components/admin/Title";
import { KeyIcon } from "@/components/icons/icons";
import { errorHandlingFetcher } from "@/lib/fetcher";
import { ErrorCallout } from "@/components/ErrorCallout";
import useSWR, { mutate } from "swr";
import { Separator } from "@/components/ui/separator";
import {
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
Table,
} from "@/components/ui/table";
import Text from "@/components/ui/text";
import Title from "@/components/ui/title";
import { usePopup } from "@/components/admin/connectors/Popup";
import { useState } from "react";
import { DeleteButton } from "@/components/DeleteButton";
import { FiCopy, FiEdit2, FiRefreshCw } from "react-icons/fi";
import { Modal } from "@/components/Modal";
import { Spinner } from "@/components/Spinner";
import { deleteApiKey, regenerateApiKey } from "./lib";
import { OnyxApiKeyForm } from "./OnyxApiKeyForm";
import { APIKey } from "./types";
import CreateButton from "@/components/ui/createButton";
const API_KEY_TEXT = `API Keys allow you to access Onyx APIs programmatically. Click the button below to generate a new API Key.`;
function NewApiKeyModal({
apiKey,
onClose,
}: {
apiKey: string;
onClose: () => void;
}) {
const [copyClicked, setCopyClicked] = useState(false);
return (
<Modal title="New API Key" onOutsideClick={onClose}>
<div className="px-8 py-8">
<div className="h-32">
<Text className="mb-4">
Make sure you copy your new API key. You wont be able to see this
key again.
</Text>
<div className="flex mt-2">
<b className="my-auto break-all">{apiKey}</b>
<div
className="ml-2 my-auto p-2 hover:bg-accent-background-hovered rounded cursor-pointer"
onClick={() => {
setCopyClicked(true);
navigator.clipboard.writeText(apiKey);
setTimeout(() => {
setCopyClicked(false);
}, 10000);
}}
>
<FiCopy size="16" className="my-auto" />
</div>
</div>
{copyClicked && (
<Text className="text-success text-xs font-medium mt-1">
API Key copied!
</Text>
)}
</div>
</div>
</Modal>
);
}
function Main() {
const { popup, setPopup } = usePopup();
const {
data: apiKeys,
isLoading,
error,
} = useSWR<APIKey[]>("/api/admin/api-key", errorHandlingFetcher);
const [fullApiKey, setFullApiKey] = useState<string | null>(null);
const [keyIsGenerating, setKeyIsGenerating] = useState(false);
const [showCreateUpdateForm, setShowCreateUpdateForm] = useState(false);
const [selectedApiKey, setSelectedApiKey] = useState<APIKey | undefined>();
const handleEdit = (apiKey: APIKey) => {
setSelectedApiKey(apiKey);
setShowCreateUpdateForm(true);
};
if (isLoading) {
return <ThreeDotsLoader />;
}
if (!apiKeys || error) {
return (
<ErrorCallout
errorTitle="Failed to fetch API Keys"
errorMsg={error?.info?.detail || error.toString()}
/>
);
}
const newApiKeyButton = (
<CreateButton
onClick={() => setShowCreateUpdateForm(true)}
text="Create API Key"
/>
);
if (apiKeys.length === 0) {
return (
<div>
{popup}
<Text>{API_KEY_TEXT}</Text>
{newApiKeyButton}
{showCreateUpdateForm && (
<OnyxApiKeyForm
onCreateApiKey={(apiKey) => {
setFullApiKey(apiKey.api_key);
}}
onClose={() => {
setShowCreateUpdateForm(false);
setSelectedApiKey(undefined);
mutate("/api/admin/api-key");
}}
setPopup={setPopup}
apiKey={selectedApiKey}
/>
)}
</div>
);
}
return (
<div>
{popup}
{fullApiKey && (
<NewApiKeyModal
apiKey={fullApiKey}
onClose={() => setFullApiKey(null)}
/>
)}
{keyIsGenerating && <Spinner />}
<Text>{API_KEY_TEXT}</Text>
{newApiKeyButton}
<Separator />
<Title className="mt-6">Existing API Keys</Title>
<Table className="overflow-visible">
<TableHeader>
<TableRow>
<TableHead>Name</TableHead>
<TableHead>API Key</TableHead>
<TableHead>Role</TableHead>
<TableHead>Regenerate</TableHead>
<TableHead>Delete</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{apiKeys.map((apiKey) => (
<TableRow key={apiKey.api_key_id}>
<TableCell>
<div
className={`
my-auto
flex
mb-1
w-fit
hover:bg-accent-background-hovered cursor-pointer
p-2
rounded-lg
border-border
text-sm`}
onClick={() => handleEdit(apiKey)}
>
<FiEdit2 className="my-auto mr-2" />
{apiKey.api_key_name || <i>null</i>}
</div>
</TableCell>
<TableCell className="max-w-64">
{apiKey.api_key_display}
</TableCell>
<TableCell className="max-w-64">
{apiKey.api_key_role.toUpperCase()}
</TableCell>
<TableCell>
<div
className={`
my-auto
flex
mb-1
w-fit
hover:bg-accent-background-hovered cursor-pointer
p-2
rounded-lg
border-border
text-sm`}
onClick={async () => {
setKeyIsGenerating(true);
const response = await regenerateApiKey(apiKey);
setKeyIsGenerating(false);
if (!response.ok) {
const errorMsg = await response.text();
setPopup({
type: "error",
message: `Failed to regenerate API Key: ${errorMsg}`,
});
return;
}
const newKey = (await response.json()) as APIKey;
setFullApiKey(newKey.api_key);
mutate("/api/admin/api-key");
}}
>
<FiRefreshCw className="mr-1 my-auto" />
Refresh
</div>
</TableCell>
<TableCell>
<DeleteButton
onClick={async () => {
const response = await deleteApiKey(apiKey.api_key_id);
if (!response.ok) {
const errorMsg = await response.text();
setPopup({
type: "error",
message: `Failed to delete API Key: ${errorMsg}`,
});
return;
}
mutate("/api/admin/api-key");
}}
/>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
{showCreateUpdateForm && (
<OnyxApiKeyForm
onCreateApiKey={(apiKey) => {
setFullApiKey(apiKey.api_key);
}}
onClose={() => {
setShowCreateUpdateForm(false);
setSelectedApiKey(undefined);
mutate("/api/admin/api-key");
}}
setPopup={setPopup}
apiKey={selectedApiKey}
/>
)}
</div>
);
}
export default function Page() {
return (
<div className="mx-auto container">
<AdminPageTitle title="API Keys" icon={<KeyIcon size={32} />} />
<Main />
</div>
);
}