fix: treat auto login session when changed on backend .env (#2768)
* feat(App.tsx): add LANGFLOW_AUTO_LOGIN_OPTION constant to handle auto login feature
feat(authContext.tsx): add LANGFLOW_AUTO_LOGIN_OPTION constant to handle auto login feature
feat(api.tsx): add support for LANGFLOW_AUTO_LOGIN_OPTION constant to handle auto login feature
feat(LoginPage/index.tsx): add support for auto login feature in login function
feat(LoginAdminPage/index.tsx): add support for auto login feature in login function
* [autofix.ci] apply automated fixes
* 🐛 (frontend): fix potential bug by checking if response object is null before accessing status property in saveFlowToDatabase and getFlowFromDatabase functions
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
parent
785771acef
commit
e765a7fee6
9 changed files with 49 additions and 24 deletions
|
|
@ -1,4 +1,5 @@
|
||||||
import { useContext, useEffect, useState } from "react";
|
import { useContext, useEffect, useState } from "react";
|
||||||
|
import { Cookies } from "react-cookie";
|
||||||
import { ErrorBoundary } from "react-error-boundary";
|
import { ErrorBoundary } from "react-error-boundary";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import "reactflow/dist/style.css";
|
import "reactflow/dist/style.css";
|
||||||
|
|
@ -10,6 +11,7 @@ import LoadingComponent from "./components/loadingComponent";
|
||||||
import {
|
import {
|
||||||
FETCH_ERROR_DESCRIPION,
|
FETCH_ERROR_DESCRIPION,
|
||||||
FETCH_ERROR_MESSAGE,
|
FETCH_ERROR_MESSAGE,
|
||||||
|
LANGFLOW_AUTO_LOGIN_OPTION,
|
||||||
} from "./constants/constants";
|
} from "./constants/constants";
|
||||||
import { AuthContext } from "./contexts/authContext";
|
import { AuthContext } from "./contexts/authContext";
|
||||||
import { autoLogin } from "./controllers/API";
|
import { autoLogin } from "./controllers/API";
|
||||||
|
|
@ -27,13 +29,14 @@ import { useFolderStore } from "./stores/foldersStore";
|
||||||
export default function App() {
|
export default function App() {
|
||||||
useTrackLastVisitedPath();
|
useTrackLastVisitedPath();
|
||||||
const isLoading = useFlowsManagerStore((state) => state.isLoading);
|
const isLoading = useFlowsManagerStore((state) => state.isLoading);
|
||||||
const { isAuthenticated, login, setUserData, setAutoLogin, getUser } =
|
const { isAuthenticated, login, setUserData, setAutoLogin, getUser, logout } =
|
||||||
useContext(AuthContext);
|
useContext(AuthContext);
|
||||||
const setLoading = useAlertStore((state) => state.setLoading);
|
const setLoading = useAlertStore((state) => state.setLoading);
|
||||||
const refreshStars = useDarkStore((state) => state.refreshStars);
|
const refreshStars = useDarkStore((state) => state.refreshStars);
|
||||||
const dark = useDarkStore((state) => state.dark);
|
const dark = useDarkStore((state) => state.dark);
|
||||||
|
|
||||||
useGetVersionQuery();
|
useGetVersionQuery();
|
||||||
|
const cookies = new Cookies();
|
||||||
|
|
||||||
const isLoadingFolders = useFolderStore((state) => state.isLoadingFolders);
|
const isLoadingFolders = useFolderStore((state) => state.isLoadingFolders);
|
||||||
|
|
||||||
|
|
@ -60,7 +63,7 @@ export default function App() {
|
||||||
.then(async (user) => {
|
.then(async (user) => {
|
||||||
if (user && user["access_token"]) {
|
if (user && user["access_token"]) {
|
||||||
user["refresh_token"] = "auto";
|
user["refresh_token"] = "auto";
|
||||||
login(user["access_token"]);
|
login(user["access_token"], "auto");
|
||||||
setUserData(user);
|
setUserData(user);
|
||||||
setAutoLogin(true);
|
setAutoLogin(true);
|
||||||
fetchAllData();
|
fetchAllData();
|
||||||
|
|
@ -69,6 +72,14 @@ export default function App() {
|
||||||
.catch(async (error) => {
|
.catch(async (error) => {
|
||||||
if (error.name !== "CanceledError") {
|
if (error.name !== "CanceledError") {
|
||||||
setAutoLogin(false);
|
setAutoLogin(false);
|
||||||
|
if (
|
||||||
|
cookies.get(LANGFLOW_AUTO_LOGIN_OPTION) === "auto" &&
|
||||||
|
isAuthenticated
|
||||||
|
) {
|
||||||
|
logout();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (isAuthenticated && !isLoginPage) {
|
if (isAuthenticated && !isLoginPage) {
|
||||||
getUser();
|
getUser();
|
||||||
fetchAllData();
|
fetchAllData();
|
||||||
|
|
|
||||||
|
|
@ -866,3 +866,7 @@ export const TABS_ORDER = [
|
||||||
"python code",
|
"python code",
|
||||||
"chat widget html",
|
"chat widget html",
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const LANGFLOW_ACCESS_TOKEN = "access_token_lf";
|
||||||
|
export const LANGFLOW_API_TOKEN = "apikey_tkn_lflw";
|
||||||
|
export const LANGFLOW_AUTO_LOGIN_OPTION = "auto_login_lf";
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
import {
|
||||||
|
LANGFLOW_ACCESS_TOKEN,
|
||||||
|
LANGFLOW_API_TOKEN,
|
||||||
|
LANGFLOW_AUTO_LOGIN_OPTION,
|
||||||
|
} from "@/constants/constants";
|
||||||
import useFlowsManagerStore from "@/stores/flowsManagerStore";
|
import useFlowsManagerStore from "@/stores/flowsManagerStore";
|
||||||
import { createContext, useEffect, useState } from "react";
|
import { createContext, useEffect, useState } from "react";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
@ -38,17 +43,17 @@ export function AuthProvider({ children }): React.ReactElement {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const cookies = new Cookies();
|
const cookies = new Cookies();
|
||||||
const [accessToken, setAccessToken] = useState<string | null>(
|
const [accessToken, setAccessToken] = useState<string | null>(
|
||||||
cookies.get("access_token_lf") ?? null,
|
cookies.get(LANGFLOW_ACCESS_TOKEN) ?? null,
|
||||||
);
|
);
|
||||||
const [isAuthenticated, setIsAuthenticated] = useState<boolean>(
|
const [isAuthenticated, setIsAuthenticated] = useState<boolean>(
|
||||||
!!cookies.get("access_token_lf"),
|
!!cookies.get(LANGFLOW_ACCESS_TOKEN),
|
||||||
);
|
);
|
||||||
const [isAdmin, setIsAdmin] = useState<boolean>(false);
|
const [isAdmin, setIsAdmin] = useState<boolean>(false);
|
||||||
const [userData, setUserData] = useState<Users | null>(null);
|
const [userData, setUserData] = useState<Users | null>(null);
|
||||||
const [autoLogin, setAutoLogin] = useState<boolean>(false);
|
const [autoLogin, setAutoLogin] = useState<boolean>(false);
|
||||||
const setLoading = useAlertStore((state) => state.setLoading);
|
const setLoading = useAlertStore((state) => state.setLoading);
|
||||||
const [apiKey, setApiKey] = useState<string | null>(
|
const [apiKey, setApiKey] = useState<string | null>(
|
||||||
cookies.get("apikey_tkn_lflw"),
|
cookies.get(LANGFLOW_API_TOKEN),
|
||||||
);
|
);
|
||||||
|
|
||||||
const getFoldersApi = useFolderStore((state) => state.getFoldersApi);
|
const getFoldersApi = useFolderStore((state) => state.getFoldersApi);
|
||||||
|
|
@ -61,14 +66,14 @@ export function AuthProvider({ children }): React.ReactElement {
|
||||||
const setSelectedFolder = useFolderStore((state) => state.setSelectedFolder);
|
const setSelectedFolder = useFolderStore((state) => state.setSelectedFolder);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const storedAccessToken = cookies.get("access_token_lf");
|
const storedAccessToken = cookies.get(LANGFLOW_ACCESS_TOKEN);
|
||||||
if (storedAccessToken) {
|
if (storedAccessToken) {
|
||||||
setAccessToken(storedAccessToken);
|
setAccessToken(storedAccessToken);
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const apiKey = cookies.get("apikey_tkn_lflw");
|
const apiKey = cookies.get(LANGFLOW_API_TOKEN);
|
||||||
if (apiKey) {
|
if (apiKey) {
|
||||||
setApiKey(apiKey);
|
setApiKey(apiKey);
|
||||||
}
|
}
|
||||||
|
|
@ -91,7 +96,8 @@ export function AuthProvider({ children }): React.ReactElement {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function login(newAccessToken: string) {
|
function login(newAccessToken: string, autoLogin: string) {
|
||||||
|
cookies.set(LANGFLOW_AUTO_LOGIN_OPTION, autoLogin, { path: "/" });
|
||||||
setAccessToken(newAccessToken);
|
setAccessToken(newAccessToken);
|
||||||
setIsAuthenticated(true);
|
setIsAuthenticated(true);
|
||||||
getUser();
|
getUser();
|
||||||
|
|
@ -103,7 +109,8 @@ export function AuthProvider({ children }): React.ReactElement {
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await requestLogout();
|
await requestLogout();
|
||||||
cookies.remove("apikey_tkn_lflw", { path: "/" });
|
cookies.remove(LANGFLOW_API_TOKEN, { path: "/" });
|
||||||
|
cookies.remove(LANGFLOW_AUTO_LOGIN_OPTION, { path: "/" });
|
||||||
setIsAdmin(false);
|
setIsAdmin(false);
|
||||||
setUserData(null);
|
setUserData(null);
|
||||||
setAccessToken(null);
|
setAccessToken(null);
|
||||||
|
|
@ -111,6 +118,7 @@ export function AuthProvider({ children }): React.ReactElement {
|
||||||
setAllFlows([]);
|
setAllFlows([]);
|
||||||
setSelectedFolder(null);
|
setSelectedFolder(null);
|
||||||
navigate("/login");
|
navigate("/login");
|
||||||
|
useFlowsManagerStore.setState({ isLoading: false });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
throw error;
|
throw error;
|
||||||
|
|
@ -118,7 +126,7 @@ export function AuthProvider({ children }): React.ReactElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
function storeApiKey(apikey: string) {
|
function storeApiKey(apikey: string) {
|
||||||
cookies.set("apikey_tkn_lflw", apikey, { path: "/" });
|
cookies.set(LANGFLOW_ACCESS_TOKEN, apikey, { path: "/" });
|
||||||
setApiKey(apikey);
|
setApiKey(apikey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,7 @@
|
||||||
|
import {
|
||||||
|
LANGFLOW_ACCESS_TOKEN,
|
||||||
|
LANGFLOW_AUTO_LOGIN_OPTION,
|
||||||
|
} from "@/constants/constants";
|
||||||
import axios, { AxiosError, AxiosInstance } from "axios";
|
import axios, { AxiosError, AxiosInstance } from "axios";
|
||||||
import { useContext, useEffect } from "react";
|
import { useContext, useEffect } from "react";
|
||||||
import { Cookies } from "react-cookie";
|
import { Cookies } from "react-cookie";
|
||||||
|
|
@ -37,7 +41,7 @@ function ApiInterceptor() {
|
||||||
}
|
}
|
||||||
const acceptedRequest = await tryToRenewAccessToken(error);
|
const acceptedRequest = await tryToRenewAccessToken(error);
|
||||||
|
|
||||||
const accessToken = cookies.get("access_token_lf");
|
const accessToken = cookies.get(LANGFLOW_ACCESS_TOKEN);
|
||||||
|
|
||||||
if (!accessToken && error?.config?.url?.includes("login")) {
|
if (!accessToken && error?.config?.url?.includes("login")) {
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
|
|
@ -90,7 +94,7 @@ function ApiInterceptor() {
|
||||||
console.error("Duplicate Request");
|
console.error("Duplicate Request");
|
||||||
}
|
}
|
||||||
|
|
||||||
const accessToken = cookies.get("access_token_lf");
|
const accessToken = cookies.get(LANGFLOW_ACCESS_TOKEN);
|
||||||
if (accessToken && !isAuthorizedURL(config?.url)) {
|
if (accessToken && !isAuthorizedURL(config?.url)) {
|
||||||
config.headers["Authorization"] = `Bearer ${accessToken}`;
|
config.headers["Authorization"] = `Bearer ${accessToken}`;
|
||||||
}
|
}
|
||||||
|
|
@ -129,7 +133,7 @@ function ApiInterceptor() {
|
||||||
if (window.location.pathname.includes("/login")) return;
|
if (window.location.pathname.includes("/login")) return;
|
||||||
const res = await renewAccessToken();
|
const res = await renewAccessToken();
|
||||||
if (res?.data?.access_token && res?.data?.refresh_token) {
|
if (res?.data?.access_token && res?.data?.refresh_token) {
|
||||||
login(res?.data?.access_token);
|
login(res?.data?.access_token, cookies.get(LANGFLOW_AUTO_LOGIN_OPTION));
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
clearBuildVerticesState(error);
|
clearBuildVerticesState(error);
|
||||||
|
|
|
||||||
|
|
@ -140,8 +140,8 @@ export async function saveFlowToDatabase(newFlow: {
|
||||||
endpoint_name: newFlow.endpoint_name,
|
endpoint_name: newFlow.endpoint_name,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.status !== 201) {
|
if (response?.status !== 201) {
|
||||||
throw new Error(`HTTP error! status: ${response.status}`);
|
throw new Error(`HTTP error! status: ${response?.status}`);
|
||||||
}
|
}
|
||||||
return response.data;
|
return response.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
@ -254,8 +254,8 @@ export async function deleteFlowFromDatabase(flowId: string) {
|
||||||
export async function getFlowFromDatabase(flowId: number) {
|
export async function getFlowFromDatabase(flowId: number) {
|
||||||
try {
|
try {
|
||||||
const response = await api.get(`${BASE_URL_API}flows/${flowId}`);
|
const response = await api.get(`${BASE_URL_API}flows/${flowId}`);
|
||||||
if (response.status !== 200) {
|
if (response?.status !== 200) {
|
||||||
throw new Error(`HTTP error! status: ${response.status}`);
|
throw new Error(`HTTP error! status: ${response?.status}`);
|
||||||
}
|
}
|
||||||
return response.data;
|
return response.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ export default function LoginAdminPage() {
|
||||||
|
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
login(user.access_token);
|
login(user.access_token, "login");
|
||||||
navigate("/admin/");
|
navigate("/admin/");
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
|
|
|
||||||
|
|
@ -39,10 +39,8 @@ export default function LoginPage(): JSX.Element {
|
||||||
};
|
};
|
||||||
onLogin(user)
|
onLogin(user)
|
||||||
.then((user) => {
|
.then((user) => {
|
||||||
console.log("login page");
|
|
||||||
|
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
login(user.access_token);
|
login(user.access_token, "login");
|
||||||
navigate("/");
|
navigate("/");
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ export const useFolderStore = create<FoldersStoreType>((set, get) => ({
|
||||||
(folder) => folder.name === STARTER_FOLDER_NAME,
|
(folder) => folder.name === STARTER_FOLDER_NAME,
|
||||||
);
|
);
|
||||||
|
|
||||||
set({ starterProjectId: starterProjects!.id ?? "" });
|
set({ starterProjectId: starterProjects?.id ?? "" });
|
||||||
set({ folders: foldersWithoutStarterProjects });
|
set({ folders: foldersWithoutStarterProjects });
|
||||||
|
|
||||||
const myCollectionId = res?.find(
|
const myCollectionId = res?.find(
|
||||||
|
|
@ -72,7 +72,7 @@ export const useFolderStore = create<FoldersStoreType>((set, get) => ({
|
||||||
(folder) => folder.name === STARTER_FOLDER_NAME,
|
(folder) => folder.name === STARTER_FOLDER_NAME,
|
||||||
);
|
);
|
||||||
|
|
||||||
set({ starterProjectId: starterProjects!.id ?? "" });
|
set({ starterProjectId: starterProjects?.id ?? "" });
|
||||||
set({ folders: foldersWithoutStarterProjects });
|
set({ folders: foldersWithoutStarterProjects });
|
||||||
|
|
||||||
const myCollectionId = res?.find(
|
const myCollectionId = res?.find(
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ export type AuthContextType = {
|
||||||
setIsAdmin: (isAdmin: boolean) => void;
|
setIsAdmin: (isAdmin: boolean) => void;
|
||||||
isAuthenticated: boolean;
|
isAuthenticated: boolean;
|
||||||
accessToken: string | null;
|
accessToken: string | null;
|
||||||
login: (accessToken: string) => void;
|
login: (accessToken: string, autoLogin: string) => void;
|
||||||
logout: () => Promise<void>;
|
logout: () => Promise<void>;
|
||||||
userData: Users | null;
|
userData: Users | null;
|
||||||
setUserData: (userData: Users | null) => void;
|
setUserData: (userData: Users | null) => void;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue