Refactored API Key handling
This commit is contained in:
parent
42b0f70f81
commit
dcde9b7ae3
8 changed files with 69 additions and 231 deletions
|
|
@ -561,10 +561,6 @@ export const CONTROL_NEW_USER = {
|
|||
is_superuser: false,
|
||||
};
|
||||
|
||||
export const CONTROL_NEW_API_KEY = {
|
||||
apikeyname: "",
|
||||
};
|
||||
|
||||
export const tabsCode = [];
|
||||
|
||||
export function tabsArray(codes: string[], method: number) {
|
||||
|
|
|
|||
|
|
@ -1,34 +1,24 @@
|
|||
import { createContext, useEffect, useState } from "react";
|
||||
import {
|
||||
checkHasApiKey,
|
||||
checkHasStore,
|
||||
getStoreComponents,
|
||||
} from "../controllers/API";
|
||||
import { checkHasApiKey, checkHasStore } from "../controllers/API";
|
||||
import { storeContextType } from "../types/contexts/store";
|
||||
|
||||
//store context to share user components and update them
|
||||
const initialValue = {
|
||||
savedFlows: new Set<string>(),
|
||||
setSavedFlows: () => {},
|
||||
hasStore: true,
|
||||
setHasStore: () => {},
|
||||
validApiKey: false,
|
||||
setValidApiKey: () => {},
|
||||
hasApiKey: false,
|
||||
setHasApiKey: () => {},
|
||||
getSavedComponents: () => {},
|
||||
errorApiKey: false,
|
||||
loadingSaved: false,
|
||||
};
|
||||
|
||||
export const StoreContext = createContext<storeContextType>(initialValue);
|
||||
|
||||
export function StoreProvider({ children }) {
|
||||
const [savedFlows, setSavedFlows] = useState<Set<string>>(new Set());
|
||||
|
||||
const [hasStore, setHasStore] = useState(true);
|
||||
const [hasApiKey, setHasApiKey] = useState(false);
|
||||
const [validApiKey, setValidApiKey] = useState(false);
|
||||
const [storeChecked, setStoreChecked] = useState(false);
|
||||
const [loadingSaved, setLoadingSaved] = useState(false);
|
||||
const [errorApiKey, setErrorApiKey] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchStoreData = async () => {
|
||||
|
|
@ -43,36 +33,8 @@ export function StoreProvider({ children }) {
|
|||
};
|
||||
|
||||
fetchStoreData();
|
||||
getSavedComponents();
|
||||
}, []);
|
||||
|
||||
function getSavedComponents() {
|
||||
setLoadingSaved(true);
|
||||
getStoreComponents({
|
||||
sort: "-count(liked_by)",
|
||||
filterByUser: true,
|
||||
})
|
||||
.then((data) => {
|
||||
if (data?.authorized === false) {
|
||||
setErrorApiKey(true);
|
||||
setSavedFlows(new Set<string>());
|
||||
} else {
|
||||
let savedIds = new Set<string>();
|
||||
let results = data?.results ?? [];
|
||||
results.forEach((flow) => {
|
||||
savedIds.add(flow.id);
|
||||
});
|
||||
setSavedFlows(savedIds);
|
||||
setErrorApiKey(false);
|
||||
setLoadingSaved(false);
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
setSavedFlows(new Set<string>());
|
||||
setErrorApiKey(true);
|
||||
});
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const fetchStoreData = async () => {
|
||||
try {
|
||||
|
|
@ -80,26 +42,26 @@ export function StoreProvider({ children }) {
|
|||
const res = await checkHasApiKey();
|
||||
console.log(res);
|
||||
setHasApiKey(res?.has_api_key ?? false);
|
||||
if (!res?.has_api_key) {
|
||||
setValidApiKey(false);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
};
|
||||
|
||||
fetchStoreData();
|
||||
}, [storeChecked]);
|
||||
}, [storeChecked, validApiKey]);
|
||||
|
||||
return (
|
||||
<StoreContext.Provider
|
||||
value={{
|
||||
savedFlows,
|
||||
setSavedFlows,
|
||||
hasStore,
|
||||
setHasStore,
|
||||
hasApiKey,
|
||||
setHasApiKey,
|
||||
getSavedComponents,
|
||||
errorApiKey,
|
||||
loadingSaved,
|
||||
validApiKey,
|
||||
setValidApiKey,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
|
|
|||
|
|
@ -3,14 +3,9 @@ import { useContext, useEffect, useRef, useState } from "react";
|
|||
import IconComponent from "../../components/genericIconComponent";
|
||||
import { Button } from "../../components/ui/button";
|
||||
import { Input } from "../../components/ui/input";
|
||||
import { CONTROL_NEW_API_KEY } from "../../constants/constants";
|
||||
import { alertContext } from "../../contexts/alertContext";
|
||||
import { createApiKey } from "../../controllers/API";
|
||||
import {
|
||||
ApiKeyInputType,
|
||||
ApiKeyType,
|
||||
inputHandlerEventType,
|
||||
} from "../../types/components";
|
||||
import { ApiKeyType } from "../../types/components";
|
||||
import { nodeIconsLucide } from "../../utils/styleUtils";
|
||||
import BaseModal from "../baseModal";
|
||||
|
||||
|
|
@ -27,19 +22,11 @@ export default function SecretKeyModal({
|
|||
const [open, setOpen] = useState(false);
|
||||
const [apiKeyName, setApiKeyName] = useState(data?.apikeyname ?? "");
|
||||
const [apiKeyValue, setApiKeyValue] = useState("");
|
||||
const [inputState, setInputState] =
|
||||
useState<ApiKeyInputType>(CONTROL_NEW_API_KEY);
|
||||
const [renderKey, setRenderKey] = useState(false);
|
||||
const [textCopied, setTextCopied] = useState(true);
|
||||
const { setSuccessData } = useContext(alertContext);
|
||||
const inputRef = useRef<HTMLInputElement | null>(null);
|
||||
|
||||
function handleInput({
|
||||
target: { name, value },
|
||||
}: inputHandlerEventType): void {
|
||||
setInputState((prev) => ({ ...prev, [name]: value }));
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
setRenderKey(false);
|
||||
|
|
@ -101,14 +88,7 @@ export default function SecretKeyModal({
|
|||
</span>
|
||||
<div className="flex pt-3">
|
||||
<div className="w-full">
|
||||
<Input
|
||||
ref={inputRef}
|
||||
onChange={(event) => {
|
||||
setApiKeyValue(event.target.value);
|
||||
}}
|
||||
readOnly={true}
|
||||
value={apiKeyValue}
|
||||
/>
|
||||
<Input ref={inputRef} readOnly={true} value={apiKeyValue} />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
|
@ -153,7 +133,6 @@ export default function SecretKeyModal({
|
|||
<Form.Control asChild>
|
||||
<input
|
||||
onChange={({ target: { value } }) => {
|
||||
handleInput({ target: { name: "apikeyname", value } });
|
||||
setApiKeyName(value);
|
||||
}}
|
||||
value={apiKeyName}
|
||||
|
|
|
|||
|
|
@ -1,57 +1,31 @@
|
|||
import * as Form from "@radix-ui/react-form";
|
||||
import { useContext, useEffect, useRef, useState } from "react";
|
||||
import { useContext, useState } from "react";
|
||||
import IconComponent from "../../components/genericIconComponent";
|
||||
import { Button } from "../../components/ui/button";
|
||||
import { Input } from "../../components/ui/input";
|
||||
import { CONTROL_NEW_API_KEY } from "../../constants/constants";
|
||||
import { alertContext } from "../../contexts/alertContext";
|
||||
import { AuthContext } from "../../contexts/authContext";
|
||||
import { StoreContext } from "../../contexts/storeContext";
|
||||
import { addApiKeyStore } from "../../controllers/API";
|
||||
import {
|
||||
ApiKeyInputType,
|
||||
StoreApiKeyType,
|
||||
inputHandlerEventType,
|
||||
} from "../../types/components";
|
||||
import { StoreApiKeyType } from "../../types/components";
|
||||
import BaseModal from "../baseModal";
|
||||
|
||||
export default function StoreApiKeyModal({
|
||||
children,
|
||||
onCloseModal,
|
||||
}: StoreApiKeyType) {
|
||||
export default function StoreApiKeyModal({ children }: StoreApiKeyType) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [inputState, setInputState] =
|
||||
useState<ApiKeyInputType>(CONTROL_NEW_API_KEY);
|
||||
const { setSuccessData, setErrorData } = useContext(alertContext);
|
||||
const inputRef = useRef<HTMLInputElement | null>(null);
|
||||
const { storeApiKey } = useContext(AuthContext);
|
||||
const { hasApiKey } = useContext(StoreContext);
|
||||
const [apiKeyValue, setApiKeyValue] = useState(
|
||||
hasApiKey ? "This is not a real api key." : ""
|
||||
);
|
||||
|
||||
function handleInput({
|
||||
target: { name, value },
|
||||
}: inputHandlerEventType): void {
|
||||
setInputState((prev) => ({ ...prev, [name]: value }));
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
// resetForm();
|
||||
} else {
|
||||
onCloseModal();
|
||||
}
|
||||
}, [open]);
|
||||
const { hasApiKey, setHasApiKey, validApiKey } = useContext(StoreContext);
|
||||
const [apiKeyValue, setApiKeyValue] = useState("");
|
||||
|
||||
const handleSaveKey = () => {
|
||||
if (inputState && inputState["apikey"]) {
|
||||
addApiKeyStore(inputState["apikey"]).then(
|
||||
if (apiKeyValue) {
|
||||
addApiKeyStore(apiKeyValue).then(
|
||||
() => {
|
||||
setSuccessData({
|
||||
title: "Success! Your API Key has been saved.",
|
||||
});
|
||||
storeApiKey(inputState["apikey"]);
|
||||
storeApiKey(apiKeyValue);
|
||||
setHasApiKey(true);
|
||||
setOpen(false);
|
||||
},
|
||||
(error) => {
|
||||
|
|
@ -67,7 +41,15 @@ export default function StoreApiKeyModal({
|
|||
return (
|
||||
<BaseModal size="small-h-full" open={open} setOpen={setOpen}>
|
||||
<BaseModal.Trigger>{children}</BaseModal.Trigger>
|
||||
<BaseModal.Header description={"Insert your Langflow API key."}>
|
||||
<BaseModal.Header
|
||||
description={
|
||||
(hasApiKey && !validApiKey
|
||||
? "Your API key is not valid. "
|
||||
: !hasApiKey
|
||||
? "You don't have an API key. "
|
||||
: "") + "Insert your Langflow API key."
|
||||
}
|
||||
>
|
||||
<span className="pr-2">API Key</span>
|
||||
<IconComponent
|
||||
name="Key"
|
||||
|
|
@ -82,7 +64,7 @@ export default function StoreApiKeyModal({
|
|||
}}
|
||||
>
|
||||
<div className="grid gap-5">
|
||||
<Form.Field name="username">
|
||||
<Form.Field name="apikey">
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<Form.Control asChild>
|
||||
<Input
|
||||
|
|
@ -90,7 +72,6 @@ export default function StoreApiKeyModal({
|
|||
value={apiKeyValue}
|
||||
type="password"
|
||||
onChange={({ target: { value } }) => {
|
||||
handleInput({ target: { name: "apikey", value } });
|
||||
setApiKeyValue(value);
|
||||
}}
|
||||
placeholder="Insert your API Key"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { useContext, useEffect, useState } from "react";
|
||||
import { useContext, useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import ShadTooltip from "../../../components/ShadTooltipComponent";
|
||||
import IconComponent from "../../../components/genericIconComponent";
|
||||
|
|
@ -13,12 +13,7 @@ import {
|
|||
} from "../../../components/ui/card";
|
||||
import { alertContext } from "../../../contexts/alertContext";
|
||||
import { FlowsContext } from "../../../contexts/flowsContext";
|
||||
import { StoreContext } from "../../../contexts/storeContext";
|
||||
import {
|
||||
getComponent,
|
||||
postLikeComponent,
|
||||
saveFlowStore,
|
||||
} from "../../../controllers/API";
|
||||
import { getComponent, postLikeComponent } from "../../../controllers/API";
|
||||
import { storeComponent } from "../../../types/store";
|
||||
import cloneFLowWithParent from "../../../utils/storeUtils";
|
||||
import { gradients } from "../../../utils/styleUtils";
|
||||
|
|
@ -28,16 +23,11 @@ export const MarketCardComponent = ({
|
|||
data,
|
||||
authorized = true,
|
||||
disabled = false,
|
||||
installable = false,
|
||||
}: {
|
||||
data: storeComponent;
|
||||
authorized?: boolean;
|
||||
disabled?: boolean;
|
||||
installable?: boolean;
|
||||
}) => {
|
||||
const { savedFlows } = useContext(StoreContext);
|
||||
const [added, setAdded] = useState(savedFlows.has(data.id) ? true : false);
|
||||
const [installed, setInstalled] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const { addFlow } = useContext(FlowsContext);
|
||||
const [loadingLike, setLoadingLike] = useState(false);
|
||||
|
|
@ -47,40 +37,6 @@ export const MarketCardComponent = ({
|
|||
|
||||
const name = data.is_component ? "Component" : "Flow";
|
||||
|
||||
useEffect(() => {
|
||||
setAdded(savedFlows.has(data.id) ? true : false);
|
||||
}, [savedFlows]);
|
||||
|
||||
function handleAdd() {
|
||||
setLoading(true);
|
||||
getComponent(data.id).then(
|
||||
(res) => {
|
||||
console.log(res);
|
||||
const newFLow = cloneFLowWithParent(res, res.id, data.is_component);
|
||||
console.log(newFLow);
|
||||
saveFlowStore(
|
||||
newFLow,
|
||||
data.tags.map((tag) => tag.id)
|
||||
)
|
||||
.then(() => {
|
||||
setAdded(true);
|
||||
setLoading(false);
|
||||
setSuccessData({ title: `${name} added to account.` });
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
setErrorData({
|
||||
title: `Error on adding ${name}`,
|
||||
list: [error["response"]["data"]["detail"]],
|
||||
});
|
||||
});
|
||||
},
|
||||
(error) => {
|
||||
console.log(error);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
function handleInstall() {
|
||||
|
|
@ -90,7 +46,6 @@ export const MarketCardComponent = ({
|
|||
addFlow(true, newFlow).then((id) => {
|
||||
setSuccessData({ title: `${name} Installed` });
|
||||
setLoading(false);
|
||||
setInstalled(true);
|
||||
if (!data.is_component) navigate("/flow/" + id);
|
||||
});
|
||||
});
|
||||
|
|
@ -290,13 +245,7 @@ export const MarketCardComponent = ({
|
|||
</ShadTooltip>
|
||||
<ShadTooltip
|
||||
content={
|
||||
authorized
|
||||
? added
|
||||
? installable
|
||||
? "Install Locally"
|
||||
: "Added to Account"
|
||||
: "Add to Account"
|
||||
: "Please review your API key."
|
||||
authorized ? "Install Locally" : "Please review your API key."
|
||||
}
|
||||
>
|
||||
<Button
|
||||
|
|
@ -310,34 +259,13 @@ export const MarketCardComponent = ({
|
|||
if (loading || !authorized) {
|
||||
return;
|
||||
}
|
||||
if (!added) {
|
||||
handleAdd();
|
||||
} else if (installable) {
|
||||
handleInstall();
|
||||
}
|
||||
handleInstall();
|
||||
}}
|
||||
>
|
||||
<IconComponent
|
||||
name={
|
||||
loading
|
||||
? "Loader2"
|
||||
: added && installable && !installed
|
||||
? "ExternalLink"
|
||||
: added
|
||||
? "Check"
|
||||
: "Plus"
|
||||
}
|
||||
name={loading ? "Loader2" : "Plus"}
|
||||
className={classNames(
|
||||
!added && !installable && !loading
|
||||
? "h-6 w-6"
|
||||
: "h-5 w-5",
|
||||
((added && !installable) || installed) && !loading
|
||||
? "text-chat-send"
|
||||
: "",
|
||||
added && installable && !installed && !loading
|
||||
? "text-high-indigo"
|
||||
: "",
|
||||
loading ? " animate-spin" : "",
|
||||
loading ? "h-5 w-5 animate-spin" : "h-6 w-6 p-0.5",
|
||||
!authorized ? " text-ring" : ""
|
||||
)}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ import { storeComponent } from "../../types/store";
|
|||
import { cn } from "../../utils/utils";
|
||||
import { MarketCardComponent } from "./components/market-card";
|
||||
export default function StorePage(): JSX.Element {
|
||||
const { errorApiKey, hasApiKey, setHasApiKey } = useContext(StoreContext);
|
||||
const { validApiKey, setValidApiKey, hasApiKey } = useContext(StoreContext);
|
||||
const { setErrorData } = useContext(alertContext);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [loadingTags, setLoadingTags] = useState(true);
|
||||
|
|
@ -52,7 +52,7 @@ export default function StorePage(): JSX.Element {
|
|||
pageSize,
|
||||
filteredCategories,
|
||||
selectFilter,
|
||||
hasApiKey,
|
||||
validApiKey,
|
||||
]);
|
||||
|
||||
function handleGetTags() {
|
||||
|
|
@ -72,29 +72,36 @@ export default function StorePage(): JSX.Element {
|
|||
tabActive === "All" ? null : tabActive === "Flows" ? false : true,
|
||||
sort: pageOrder === "Popular" ? "-count(downloads)" : "name",
|
||||
tags: filteredCategories,
|
||||
liked: selectFilter === "likedbyme" && hasApiKey ? true : null,
|
||||
liked: selectFilter === "likedbyme" && validApiKey ? true : null,
|
||||
status: null,
|
||||
search: searchText === "" ? null : searchText,
|
||||
filterByUser: selectFilter === "createdbyme" && hasApiKey ? true : null,
|
||||
filterByUser: selectFilter === "createdbyme" && validApiKey ? true : null,
|
||||
})
|
||||
.then((res) => {
|
||||
setHasApiKey(res?.authorized ?? false);
|
||||
setLoading(false);
|
||||
setSearchData(res?.results ?? []);
|
||||
setTotalRowsCount(
|
||||
filteredCategories?.length === 0
|
||||
? Number(res?.count ?? 0)
|
||||
: res?.results?.length ?? 0
|
||||
);
|
||||
if (!res?.authorized && validApiKey === true) {
|
||||
setValidApiKey(false);
|
||||
} else {
|
||||
setLoading(false);
|
||||
setSearchData(res?.results ?? []);
|
||||
setTotalRowsCount(
|
||||
filteredCategories?.length === 0
|
||||
? Number(res?.count ?? 0)
|
||||
: res?.results?.length ?? 0
|
||||
);
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
setSearchData([]);
|
||||
setTotalRowsCount(0);
|
||||
setLoading(false);
|
||||
setErrorData({
|
||||
title: "Error to get components.",
|
||||
list: [err["response"]["data"]["detail"]],
|
||||
});
|
||||
if (err.response.status === 403) {
|
||||
setValidApiKey(false);
|
||||
} else {
|
||||
setSearchData([]);
|
||||
setTotalRowsCount(0);
|
||||
setLoading(false);
|
||||
setErrorData({
|
||||
title: "Error to get components.",
|
||||
list: [err["response"]["data"]["detail"]],
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -123,17 +130,10 @@ export default function StorePage(): JSX.Element {
|
|||
Langflow Store
|
||||
</span>
|
||||
<div className="community-page-nav-button">
|
||||
<StoreApiKeyModal
|
||||
onCloseModal={() => {
|
||||
handleGetTags();
|
||||
handleGetComponents();
|
||||
}}
|
||||
>
|
||||
<StoreApiKeyModal>
|
||||
<Button
|
||||
className={`${
|
||||
errorApiKey && !hasApiKey
|
||||
? "animate-pulse border-error"
|
||||
: ""
|
||||
!validApiKey ? "animate-pulse border-error" : ""
|
||||
}`}
|
||||
variant="primary"
|
||||
>
|
||||
|
|
@ -303,7 +303,7 @@ export default function StorePage(): JSX.Element {
|
|||
<MarketCardComponent
|
||||
key={item.id}
|
||||
data={item}
|
||||
authorized={hasApiKey}
|
||||
authorized={validApiKey}
|
||||
disabled={loading}
|
||||
/>
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -269,7 +269,7 @@ export type signUpInputStateType = {
|
|||
|
||||
export type inputHandlerEventType = {
|
||||
target: {
|
||||
value: string | boolean;
|
||||
value: string;
|
||||
name: string;
|
||||
};
|
||||
};
|
||||
|
|
@ -356,11 +356,6 @@ export type ApiKeyType = {
|
|||
|
||||
export type StoreApiKeyType = {
|
||||
children: ReactElement;
|
||||
onCloseModal: () => void;
|
||||
};
|
||||
|
||||
export type ApiKeyInputType = {
|
||||
apikeyname: string;
|
||||
};
|
||||
export type groupedObjType = {
|
||||
family: string;
|
||||
|
|
|
|||
|
|
@ -1,11 +1,8 @@
|
|||
export type storeContextType = {
|
||||
savedFlows: Set<string>;
|
||||
setSavedFlows: (newState: Set<string>) => void;
|
||||
setHasStore: (store: boolean) => void;
|
||||
hasStore: boolean;
|
||||
setHasApiKey: (key: boolean) => void;
|
||||
hasApiKey: boolean;
|
||||
getSavedComponents: () => void;
|
||||
errorApiKey: boolean;
|
||||
loadingSaved: boolean;
|
||||
setValidApiKey: (key: boolean) => void;
|
||||
validApiKey: boolean;
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue