core/apps/webapp/app/routes/settings.api.tsx
Harshith Mullapudi 54e535d57d
Feat: v2 (#12)
* Feat: v2

* feat: add chat functionality

* First cut: integrations

* Feat: add conversation API

* Enhance conversation handling and memory management

* Feat: added conversation

---------

Co-authored-by: Manoj K <saimanoj58@gmail.com>
2025-07-08 22:41:00 +05:30

185 lines
5.6 KiB
TypeScript

import {
type LoaderFunctionArgs,
type ActionFunctionArgs,
} from "@remix-run/server-runtime";
import { Plus, Copy } from "lucide-react";
import { Button } from "~/components/ui";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "~/components/ui/dialog";
import { useFetcher } from "@remix-run/react";
import { Input } from "~/components/ui/input";
import { useState } from "react";
import { parse } from "@conform-to/zod";
import { json } from "@remix-run/node";
import { z } from "zod";
import {
createPersonalAccessToken,
getValidPersonalAccessTokens,
revokePersonalAccessToken,
} from "~/services/personalAccessToken.server";
import { requireUserId } from "~/services/session.server";
import { useTypedLoaderData } from "remix-typedjson";
import { APITable } from "~/components/api";
export const APIKeyBodyRequest = z.object({
name: z.string(),
});
export const APIKeyDeleteBodyRequest = z.object({
id: z.string(),
});
export async function action({ request }: ActionFunctionArgs) {
const userId = await requireUserId(request);
if (request.method === "DELETE") {
const formData = await request.formData();
const submission = parse(formData, {
schema: APIKeyDeleteBodyRequest,
});
if (!submission.value || submission.intent !== "submit") {
return json(submission);
}
const results = await revokePersonalAccessToken(submission.value.id);
return json(results);
}
const formData = await request.formData();
const submission = parse(formData, {
schema: APIKeyBodyRequest,
});
if (!submission.value || submission.intent !== "submit") {
return json(submission);
}
const results = await createPersonalAccessToken({
name: submission.value.name,
userId,
});
return json(results);
}
export async function loader({ request }: LoaderFunctionArgs) {
const userId = await requireUserId(request);
const personalAccessTokens = await getValidPersonalAccessTokens(userId);
return personalAccessTokens;
}
export default function API() {
const personalAccessTokens = useTypedLoaderData<typeof loader>();
const [open, setOpen] = useState(false);
const [showToken, setShowToken] = useState(false);
const fetcher = useFetcher<{ token: string }>();
const isSubmitting = fetcher.state !== "idle";
const [name, setName] = useState("");
const onSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
fetcher.submit({ name }, { method: "POST", action: "/settings/api" });
setOpen(false);
setShowToken(true);
};
const copyToClipboard = (text: string | undefined) => {
text && navigator.clipboard.writeText(text);
};
return (
<div className="home flex h-full flex-col overflow-y-auto p-3">
<div className="flex items-center justify-between">
<div className="space-y-1 text-base">
<h2 className="text-lg font-semibold">API Keys</h2>
<p className="text-muted-foreground">
Create and manage API keys to access your data programmatically. API
keys allow secure access to your workspace's data and functionality
through our REST API.
</p>
</div>
<div>
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
<Button
className="inline-flex items-center justify-center gap-1"
variant="secondary"
>
<Plus size={16} />
Create
</Button>
</DialogTrigger>
<DialogContent className="p-3">
<DialogHeader>
<DialogTitle>Create API Key</DialogTitle>
</DialogHeader>
<fetcher.Form
method="post"
onSubmit={onSubmit}
className="space-y-4"
>
<div>
<Input
id="name"
onChange={(e) => setName(e.target.value)}
name="name"
placeholder="Enter API key name"
className="mt-1"
required
/>
</div>
<div className="flex justify-end">
<Button
type="submit"
variant="secondary"
disabled={isSubmitting}
>
{isSubmitting ? "Creating..." : "Create API Key"}
</Button>
</div>
</fetcher.Form>
</DialogContent>
</Dialog>
<Dialog open={showToken} onOpenChange={setShowToken}>
<DialogContent className="p-3">
<DialogHeader>
<DialogTitle>Your New API Key</DialogTitle>
</DialogHeader>
<div className="space-y-2">
<p className="text-muted-foreground text-sm">
Make sure to copy your API key now. You won't be able to see
it again!
</p>
<div className="flex items-center gap-2 rounded-md border p-3">
<code className="flex-1 text-sm break-all">
{fetcher.data?.token}
</code>
<Button
variant="ghost"
size="sm"
onClick={() => copyToClipboard(fetcher.data?.token)}
>
<Copy size={16} />
</Button>
</div>
</div>
</DialogContent>
</Dialog>
</div>
</div>
<APITable personalAccessTokens={personalAccessTokens} />
</div>
);
}