feat: add Store API Key management functionality (#5596)
* feat: add Store API Key management functionality - Introduced a new Store API Key page for managing API keys related to the Langflow Store. - Updated routes to include the new StoreApiKeyPage and added a corresponding link in the SettingsPage. - Enhanced the API key management interface with a dedicated form for inputting and saving API keys. - Updated existing constants and titles for clarity, ensuring consistent terminology across the application. This addition improves user experience by providing a centralized location for managing store-related API keys. * feat: conditionally add Langflow API Keys section in SettingsPage - Imported ENABLE_DATASTAX_LANGFLOW feature flag to control the visibility of the Langflow API Keys section in the sidebar. - The API Keys section is now conditionally rendered based on the feature flag, enhancing the flexibility of the SettingsPage. - This change improves user experience by ensuring that only relevant options are displayed based on the current feature configuration. * feat: refactor SettingsPage to improve sidebar navigation - Consolidated the addition of Langflow API Keys and Store sections into a single array for better organization and readability. - Enhanced the conditional rendering logic for sidebar navigation items based on the ENABLE_DATASTAX_LANGFLOW feature flag. - This refactor improves maintainability and clarity of the SettingsPage component. --------- Co-authored-by: Cristhian Zanforlin Lousa <cristhian.lousa@gmail.com>
This commit is contained in:
parent
75c3c1ce97
commit
5280f396ef
8 changed files with 201 additions and 28 deletions
|
|
@ -629,7 +629,7 @@ export const TIMEOUT_ERROR_DESCRIPION = "Server is busy.";
|
|||
export const SIGN_UP_SUCCESS = "Account created! Await admin activation. ";
|
||||
|
||||
export const API_PAGE_PARAGRAPH =
|
||||
"Your secret API keys are listed below. Do not share your API key with others, or expose it in the browser or other client-side code.";
|
||||
"Your secret Langflow API keys are listed below. Do not share your API key with others, or expose it in the browser or other client-side code.";
|
||||
|
||||
export const API_PAGE_USER_KEYS =
|
||||
"This user does not have any keys assigned at the moment.";
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
import SideBarButtonsComponent from "@/components/core/sidebarComponent";
|
||||
import { SidebarProvider } from "@/components/ui/sidebar";
|
||||
import { ENABLE_PROFILE_ICONS } from "@/customization/feature-flags";
|
||||
import {
|
||||
ENABLE_DATASTAX_LANGFLOW,
|
||||
ENABLE_PROFILE_ICONS,
|
||||
} from "@/customization/feature-flags";
|
||||
import useAuthStore from "@/stores/authStore";
|
||||
import { useStoreStore } from "@/stores/storeStore";
|
||||
import { Outlet } from "react-router-dom";
|
||||
|
|
@ -44,16 +47,7 @@ export default function SettingsPage(): JSX.Element {
|
|||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Langflow API",
|
||||
href: "/settings/api-keys",
|
||||
icon: (
|
||||
<ForwardedIconComponent
|
||||
name="Key"
|
||||
className="w-4 flex-shrink-0 justify-start stroke-[1.5]"
|
||||
/>
|
||||
),
|
||||
},
|
||||
|
||||
{
|
||||
title: "Shortcuts",
|
||||
href: "/settings/shortcuts",
|
||||
|
|
@ -75,6 +69,34 @@ export default function SettingsPage(): JSX.Element {
|
|||
),
|
||||
},
|
||||
);
|
||||
|
||||
if (!ENABLE_DATASTAX_LANGFLOW) {
|
||||
const langflowItems = [
|
||||
{
|
||||
title: "Langflow API Keys",
|
||||
href: "/settings/api-keys",
|
||||
icon: (
|
||||
<ForwardedIconComponent
|
||||
name="Key"
|
||||
className="w-4 flex-shrink-0 justify-start stroke-[1.5]"
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Langflow Store",
|
||||
href: "/settings/store",
|
||||
icon: (
|
||||
<ForwardedIconComponent
|
||||
name="Store"
|
||||
className="w-4 flex-shrink-0 justify-start stroke-[1.5]"
|
||||
/>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
sidebarNavItems.splice(2, 0, ...langflowItems);
|
||||
}
|
||||
|
||||
return (
|
||||
<PageLayout
|
||||
backTo={"/"}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ const ApiKeyHeaderComponent = ({
|
|||
<div className="flex w-full items-start justify-between gap-6">
|
||||
<div className="flex w-full flex-col">
|
||||
<h2 className="flex items-center text-lg font-semibold tracking-tight">
|
||||
Langflow API
|
||||
Langflow API Keys
|
||||
<ForwardedIconComponent
|
||||
name="Key"
|
||||
className="ml-2 h-5 w-5 text-primary"
|
||||
|
|
|
|||
|
|
@ -24,10 +24,10 @@ import {
|
|||
patchUserInputStateType,
|
||||
} from "../../../../types/components";
|
||||
import useScrollToElement from "../hooks/use-scroll-to-element";
|
||||
import StoreApiKeyFormComponent from "../StoreApiKeyPage/components/StoreApiKeyForm";
|
||||
import GeneralPageHeaderComponent from "./components/GeneralPageHeader";
|
||||
import PasswordFormComponent from "./components/PasswordForm";
|
||||
import ProfilePictureFormComponent from "./components/ProfilePictureForm";
|
||||
import StoreApiKeyFormComponent from "./components/StoreApiKeyForm";
|
||||
|
||||
export const GeneralPage = () => {
|
||||
const { scrollId } = useParams();
|
||||
|
|
@ -164,16 +164,6 @@ export const GeneralPage = () => {
|
|||
handlePatchPassword={handlePatchPassword}
|
||||
/>
|
||||
)}
|
||||
{hasStore && (
|
||||
<StoreApiKeyFormComponent
|
||||
apikey={apikey}
|
||||
handleInput={handleInput}
|
||||
handleSaveKey={handleSaveKey}
|
||||
loadingApiKey={loadingApiKey}
|
||||
validApiKey={validApiKey}
|
||||
hasApiKey={hasApiKey}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import * as Form from "@radix-ui/react-form";
|
||||
import InputComponent from "../../../../../../components/core/parameterRenderComponent/components/inputComponent";
|
||||
import { Button } from "../../../../../../components/ui/button";
|
||||
import InputComponent from "../../../../../components/core/parameterRenderComponent/components/inputComponent";
|
||||
import { Button } from "../../../../../components/ui/button";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
|
|
@ -8,13 +8,13 @@ import {
|
|||
CardFooter,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "../../../../../../components/ui/card";
|
||||
} from "../../../../../components/ui/card";
|
||||
import {
|
||||
CREATE_API_KEY,
|
||||
INSERT_API_KEY,
|
||||
INVALID_API_KEY,
|
||||
NO_API_KEY,
|
||||
} from "../../../../../../constants/constants";
|
||||
} from "../../../../../constants/constants";
|
||||
|
||||
type StoreApiKeyFormComponentProps = {
|
||||
apikey: string;
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
import ForwardedIconComponent from "@/components/common/genericIconComponent";
|
||||
import { CONTROL_PATCH_USER_STATE } from "@/constants/constants";
|
||||
import { AuthContext } from "@/contexts/authContext";
|
||||
import { usePostAddApiKey } from "@/controllers/API/queries/api-keys";
|
||||
import useAlertStore from "@/stores/alertStore";
|
||||
import { useStoreStore } from "@/stores/storeStore";
|
||||
import { inputHandlerEventType } from "@/types/components";
|
||||
import { useContext, useState } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
import useScrollToElement from "../hooks/use-scroll-to-element";
|
||||
import StoreApiKeyFormComponent from "./components/StoreApiKeyForm";
|
||||
|
||||
const StoreApiKeyPage = () => {
|
||||
const { scrollId } = useParams();
|
||||
const [inputState, setInputState] = useState(CONTROL_PATCH_USER_STATE);
|
||||
const { storeApiKey } = useContext(AuthContext);
|
||||
useScrollToElement(scrollId);
|
||||
|
||||
const setSuccessData = useAlertStore((state) => state.setSuccessData);
|
||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||
const {
|
||||
validApiKey,
|
||||
hasApiKey,
|
||||
loadingApiKey,
|
||||
updateHasApiKey: setHasApiKey,
|
||||
updateValidApiKey: setValidApiKey,
|
||||
updateLoadingApiKey: setLoadingApiKey,
|
||||
} = useStoreStore();
|
||||
|
||||
const { mutate: addApiKey } = usePostAddApiKey({
|
||||
onSuccess: () => {
|
||||
setSuccessData({ title: "API key saved successfully" });
|
||||
setHasApiKey(true);
|
||||
setValidApiKey(true);
|
||||
setLoadingApiKey(false);
|
||||
handleInput({ target: { name: "apikey", value: "" } });
|
||||
},
|
||||
onError: (error) => {
|
||||
setErrorData({
|
||||
title: "API key save error",
|
||||
list: [(error as any)?.response?.data?.detail],
|
||||
});
|
||||
setHasApiKey(false);
|
||||
setValidApiKey(false);
|
||||
setLoadingApiKey(false);
|
||||
},
|
||||
});
|
||||
|
||||
const handleSaveKey = (apikey: string) => {
|
||||
if (apikey) {
|
||||
addApiKey({ key: apikey });
|
||||
storeApiKey(apikey);
|
||||
}
|
||||
};
|
||||
|
||||
const handleInput = ({ target: { name, value } }: inputHandlerEventType) => {
|
||||
setInputState((prev) => ({ ...prev, [name]: value }));
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex h-full w-full flex-col gap-6">
|
||||
<div className="flex w-full items-start gap-6">
|
||||
<div className="flex w-full flex-col">
|
||||
<h2 className="flex items-center text-lg font-semibold tracking-tight">
|
||||
Langflow Store
|
||||
<ForwardedIconComponent
|
||||
name="Store"
|
||||
className="ml-2 h-5 w-5 text-primary"
|
||||
/>
|
||||
</h2>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Manage access to the Langflow Store.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<StoreApiKeyFormComponent
|
||||
apikey={inputState.apikey}
|
||||
handleInput={handleInput}
|
||||
handleSaveKey={handleSaveKey}
|
||||
loadingApiKey={loadingApiKey}
|
||||
validApiKey={validApiKey}
|
||||
hasApiKey={hasApiKey}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default StoreApiKeyPage;
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
import { CONTROL_PATCH_USER_STATE } from "@/constants/constants";
|
||||
import { AuthContext } from "@/contexts/authContext";
|
||||
import { usePostAddApiKey } from "@/controllers/API/queries/api-keys";
|
||||
import useAlertStore from "@/stores/alertStore";
|
||||
import { useStoreStore } from "@/stores/storeStore";
|
||||
import { inputHandlerEventType } from "@/types/components";
|
||||
import { useContext, useState } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
import StoreApiKeyFormComponent from "../StoreApiKeyPage/components/StoreApiKeyForm";
|
||||
import useScrollToElement from "../hooks/use-scroll-to-element";
|
||||
|
||||
const StoreApiKeyPage = () => {
|
||||
const { scrollId } = useParams();
|
||||
const [inputState, setInputState] = useState(CONTROL_PATCH_USER_STATE);
|
||||
const { storeApiKey } = useContext(AuthContext);
|
||||
useScrollToElement(scrollId);
|
||||
|
||||
const setSuccessData = useAlertStore((state) => state.setSuccessData);
|
||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||
const {
|
||||
validApiKey,
|
||||
hasApiKey,
|
||||
loadingApiKey,
|
||||
updateHasApiKey: setHasApiKey,
|
||||
updateValidApiKey: setValidApiKey,
|
||||
updateLoadingApiKey: setLoadingApiKey,
|
||||
} = useStoreStore();
|
||||
|
||||
const { mutate: addApiKey } = usePostAddApiKey({
|
||||
onSuccess: () => {
|
||||
setSuccessData({ title: "API key saved successfully" });
|
||||
setHasApiKey(true);
|
||||
setValidApiKey(true);
|
||||
setLoadingApiKey(false);
|
||||
handleInput({ target: { name: "apikey", value: "" } });
|
||||
},
|
||||
onError: (error) => {
|
||||
setErrorData({
|
||||
title: "API key save error",
|
||||
list: [(error as any)?.response?.data?.detail],
|
||||
});
|
||||
setHasApiKey(false);
|
||||
setValidApiKey(false);
|
||||
setLoadingApiKey(false);
|
||||
},
|
||||
});
|
||||
|
||||
const handleSaveKey = (apikey: string) => {
|
||||
if (apikey) {
|
||||
addApiKey({ key: apikey });
|
||||
storeApiKey(apikey);
|
||||
}
|
||||
};
|
||||
|
||||
const handleInput = ({ target: { name, value } }: inputHandlerEventType) => {
|
||||
setInputState((prev) => ({ ...prev, [name]: value }));
|
||||
};
|
||||
|
||||
return (
|
||||
<StoreApiKeyFormComponent
|
||||
apikey={inputState.apikey}
|
||||
handleInput={handleInput}
|
||||
handleSaveKey={handleSaveKey}
|
||||
loadingApiKey={loadingApiKey}
|
||||
validApiKey={validApiKey}
|
||||
hasApiKey={hasApiKey}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default StoreApiKeyPage;
|
||||
|
|
@ -33,6 +33,7 @@ import GeneralPage from "./pages/SettingsPage/pages/GeneralPage";
|
|||
import GlobalVariablesPage from "./pages/SettingsPage/pages/GlobalVariablesPage";
|
||||
import MessagesPage from "./pages/SettingsPage/pages/messagesPage";
|
||||
import ShortcutsPage from "./pages/SettingsPage/pages/ShortcutsPage";
|
||||
import StoreApiKeyPage from "./pages/SettingsPage/pages/StoreApiKeyPage";
|
||||
import StorePage from "./pages/StorePage";
|
||||
import ViewPage from "./pages/ViewPage";
|
||||
|
||||
|
|
@ -165,6 +166,7 @@ const router = createBrowserRouter(
|
|||
/>
|
||||
<Route path="shortcuts" element={<ShortcutsPage />} />
|
||||
<Route path="messages" element={<MessagesPage />} />
|
||||
<Route path="store" element={<StoreApiKeyPage />} />
|
||||
</Route>
|
||||
<Route
|
||||
path="store"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue