Refactored API Key handling

This commit is contained in:
Lucas Oliveira 2023-11-16 17:16:50 -03:00
commit dcde9b7ae3
8 changed files with 69 additions and 231 deletions

View file

@ -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) {

View file

@ -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}

View file

@ -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}

View file

@ -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"

View file

@ -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" : ""
)}
/>

View file

@ -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}
/>
</>

View file

@ -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;

View file

@ -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;
};