From 7671f4ff7ac5b106c3faa6b59a01f154cb34be99 Mon Sep 17 00:00:00 2001 From: Mohamed Bassem Date: Sun, 14 Sep 2025 16:31:08 +0000 Subject: feat: Regen api keys --- apps/web/components/settings/AddApiKey.tsx | 25 +---- apps/web/components/settings/ApiKeySettings.tsx | 6 +- apps/web/components/settings/ApiKeySuccess.tsx | 24 +++++ apps/web/components/settings/RegenerateApiKey.tsx | 118 ++++++++++++++++++++++ apps/web/lib/i18n/locales/en/translation.json | 8 +- 5 files changed, 159 insertions(+), 22 deletions(-) create mode 100644 apps/web/components/settings/ApiKeySuccess.tsx create mode 100644 apps/web/components/settings/RegenerateApiKey.tsx (limited to 'apps/web') diff --git a/apps/web/components/settings/AddApiKey.tsx b/apps/web/components/settings/AddApiKey.tsx index 326da229..c8baa626 100644 --- a/apps/web/components/settings/AddApiKey.tsx +++ b/apps/web/components/settings/AddApiKey.tsx @@ -5,7 +5,6 @@ import { useState } from "react"; import { useRouter } from "next/navigation"; import { ActionButton } from "@/components/ui/action-button"; import { Button } from "@/components/ui/button"; -import CopyBtn from "@/components/ui/copy-button"; import { Dialog, DialogClose, @@ -33,24 +32,7 @@ import { PlusCircle } from "lucide-react"; import { useForm } from "react-hook-form"; import { z } from "zod"; -function ApiKeySuccess({ apiKey }: { apiKey: string }) { - const { t } = useTranslation(); - return ( -
-
- {t("settings.api_keys.key_success_please_copy")} -
-
- - { - return apiKey; - }} - /> -
-
- ); -} +import ApiKeySuccess from "./ApiKeySuccess"; function AddApiKeyForm({ onSuccess }: { onSuccess: (key: string) => void }) { const { t } = useTranslation(); @@ -145,7 +127,10 @@ export default function AddApiKey() { {key ? ( - + ) : ( )} diff --git a/apps/web/components/settings/ApiKeySettings.tsx b/apps/web/components/settings/ApiKeySettings.tsx index 2b9d19d1..bc4b71c5 100644 --- a/apps/web/components/settings/ApiKeySettings.tsx +++ b/apps/web/components/settings/ApiKeySettings.tsx @@ -11,6 +11,7 @@ import { api } from "@/server/api/client"; import AddApiKey from "./AddApiKey"; import DeleteApiKey from "./DeleteApiKey"; +import RegenerateApiKey from "./RegenerateApiKey"; export default async function ApiKeys() { // oxlint-disable-next-line rules-of-hooks @@ -41,7 +42,10 @@ export default async function ApiKeys() { **_{k.keyId}_** {k.createdAt.toLocaleString()} - +
+ + +
))} diff --git a/apps/web/components/settings/ApiKeySuccess.tsx b/apps/web/components/settings/ApiKeySuccess.tsx new file mode 100644 index 00000000..370d711b --- /dev/null +++ b/apps/web/components/settings/ApiKeySuccess.tsx @@ -0,0 +1,24 @@ +import CopyBtn from "@/components/ui/copy-button"; +import { Input } from "@/components/ui/input"; + +export default function ApiKeySuccess({ + apiKey, + message, +}: { + apiKey: string; + message: string; +}) { + return ( +
+
{message}
+
+ + { + return apiKey; + }} + /> +
+
+ ); +} diff --git a/apps/web/components/settings/RegenerateApiKey.tsx b/apps/web/components/settings/RegenerateApiKey.tsx new file mode 100644 index 00000000..1c034026 --- /dev/null +++ b/apps/web/components/settings/RegenerateApiKey.tsx @@ -0,0 +1,118 @@ +"use client"; + +import { useState } from "react"; +import { useRouter } from "next/navigation"; +import { ActionButton } from "@/components/ui/action-button"; +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogClose, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; +import { toast } from "@/components/ui/use-toast"; +import { useTranslation } from "@/lib/i18n/client"; +import { api } from "@/lib/trpc"; +import { RefreshCcw } from "lucide-react"; + +import ApiKeySuccess from "./ApiKeySuccess"; + +export default function RegenerateApiKey({ + id, + name, +}: { + id: string; + name: string; +}) { + const { t } = useTranslation(); + const router = useRouter(); + + const [key, setKey] = useState(undefined); + const [dialogOpen, setDialogOpen] = useState(false); + + const mutator = api.apiKeys.regenerate.useMutation({ + onSuccess: (resp) => { + setKey(resp.key); + router.refresh(); + }, + onError: () => { + toast({ + description: t("common.something_went_wrong"), + variant: "destructive", + }); + setDialogOpen(false); + }, + }); + + const handleRegenerate = () => { + mutator.mutate({ id }); + }; + + return ( + { + setDialogOpen(o); + setKey(undefined); + }} + > + + + + + + + {key + ? t("settings.api_keys.key_regenerated") + : t("settings.api_keys.regenerate_api_key")} + + {!key && ( + + {t("settings.api_keys.regenerate_warning", { name })} + + )} + + {key ? ( + + ) : ( +

+ {t("settings.api_keys.regenerate_confirmation")} +

+ )} + + {!key ? ( + <> + + + + + {t("actions.regenerate")} + + + ) : ( + + + + )} + +
+
+ ); +} diff --git a/apps/web/lib/i18n/locales/en/translation.json b/apps/web/lib/i18n/locales/en/translation.json index 065b5ed6..561c4e5a 100644 --- a/apps/web/lib/i18n/locales/en/translation.json +++ b/apps/web/lib/i18n/locales/en/translation.json @@ -80,6 +80,7 @@ "close": "Close", "merge": "Merge", "cancel": "Cancel", + "regenerate": "Regenerate", "apply_all": "Apply All", "ignore": "Ignore", "sort": { @@ -222,7 +223,12 @@ "new_api_key": "New API Key", "new_api_key_desc": "Give your API key a unique name", "key_success": "Key was successfully created", - "key_success_please_copy": "Please copy the key and store it somewhere safe. Once you close the dialog, you won't be able to access it again." + "key_success_please_copy": "Please copy the key and store it somewhere safe. Once you close the dialog, you won't be able to access it again.", + "regenerate_api_key": "Regenerate API Key", + "key_regenerated": "Key was successfully regenerated", + "key_regenerated_please_copy": "Please copy the new key and store it somewhere safe. The old key has been revoked and will no longer work.", + "regenerate_warning": "Are you sure you want to regenerate the API key \"{{name}}\"?", + "regenerate_confirmation": "This will revoke the current key and generate a new one. Any applications using the current key will stop working." }, "broken_links": { "broken_links": "Broken Links", -- cgit v1.2.3-70-g09d2