refactor: modular navigation and custom api calls (#3613)

* Added customization files

* Added custom API headers and changed logout mutation

* Changed mutationLogout on authGuard

* Renamed feature flags

* Changed auto login to run after primary loading

* Removed unused code

* Added undefined on mutationfunctiontype to have options

* Changed logout to mutationLogout

* Return data on getConfig

* Added custom wrapper

* changed hasStore to be the constant by default

* Added custom header

* Added customParam feature flag

* Fix routes and custom link and navigate to consider absolute and relative paths

* Fixed regex on  tabs search component

* Added CustomNavigate

* Added basename and ports and URLs config

* [autofix.ci] apply automated fixes

* Update config-constants.ts

* Update index.tsx

* Fixed path comparations

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
Lucas Oliveira 2024-08-29 12:05:23 -03:00 committed by GitHub
commit 06ea6c408b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
54 changed files with 300 additions and 215 deletions

View file

@ -1,9 +0,0 @@
{
"ENABLE_DARK_MODE": true,
"ENABLE_API": true,
"ENABLE_LANGFLOW_STORE": true,
"ENABLE_PROFILE_ICONS": true,
"ENABLE_SOCIAL_LINKS": true,
"ENABLE_BRANDING": true,
"ENABLE_MVPS": false
}

View file

@ -1,6 +1,7 @@
import { defineConfig, devices } from "@playwright/test";
import * as dotenv from "dotenv";
import path from "path";
import { PORT } from "./src/customization/config-constants";
dotenv.config();
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
@ -31,7 +32,7 @@ export default defineConfig({
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Base URL to use in actions like `await page.goto('/')`. */
baseURL: "http://localhost:3000/",
baseURL: `http://localhost:${PORT || 3000}/`,
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: "on-first-retry",
@ -85,7 +86,7 @@ export default defineConfig({
},
{
command: "npm start",
port: 3000,
port: PORT || 3000,
env: {
VITE_PROXY_TARGET: "http://127.0.0.1:7860",
},

View file

@ -1,6 +1,6 @@
import { CustomLink } from "@/customization/components/custom-link";
import { Transition } from "@headlessui/react";
import { useState } from "react";
import { Link } from "react-router-dom";
import IconComponent from "../../../../components/genericIconComponent";
import { SingleAlertComponentType } from "../../../../types/alerts";
@ -93,12 +93,12 @@ export default function SingleAlert({
</p>
<p className="mt-3 text-sm md:ml-6 md:mt-0">
{dropItem.link ? (
<Link
<CustomLink
to={dropItem.link}
className="whitespace-nowrap font-medium text-info-foreground hover:text-accent-foreground"
>
Details
</Link>
</CustomLink>
) : (
<></>
)}

View file

@ -1,6 +1,6 @@
import { CustomLink } from "@/customization/components/custom-link";
import { Transition } from "@headlessui/react";
import { useEffect, useState } from "react";
import { Link } from "react-router-dom";
import IconComponent from "../../components/genericIconComponent";
import { NoticeAlertType } from "../../types/alerts";
@ -52,12 +52,12 @@ export default function NoticeAlert({
</p>
<p className="mt-3 text-sm md:ml-6 md:mt-0">
{link !== "" ? (
<Link
<CustomLink
to={link}
className="whitespace-nowrap font-medium text-info-foreground hover:text-accent-foreground"
>
Details
</Link>
</CustomLink>
) : (
<></>
)}

View file

@ -1,7 +1,7 @@
import { CustomNavigate } from "@/customization/components/custom-navigate";
import { LoadingPage } from "@/pages/LoadingPage";
import useAuthStore from "@/stores/authStore";
import { useContext } from "react";
import { Navigate } from "react-router-dom";
import { AuthContext } from "../../contexts/authContext";
export const ProtectedAdminRoute = ({ children }) => {
@ -13,7 +13,7 @@ export const ProtectedAdminRoute = ({ children }) => {
if (!isAuthenticated) {
return <LoadingPage />;
} else if ((userData && !isAdmin) || autoLogin) {
return <Navigate to="/" replace />;
return <CustomNavigate to="/" replace />;
} else {
return children;
}

View file

@ -3,7 +3,10 @@ import {
LANGFLOW_ACCESS_TOKEN_EXPIRE_SECONDS_ENV,
LANGFLOW_AUTO_LOGIN_OPTION,
} from "@/constants/constants";
import { useRefreshAccessToken } from "@/controllers/API/queries/auth";
import {
useLogout,
useRefreshAccessToken,
} from "@/controllers/API/queries/auth";
import useAuthStore from "@/stores/authStore";
import { useEffect } from "react";
import { Cookies } from "react-cookie";
@ -11,11 +14,11 @@ import { Cookies } from "react-cookie";
export const ProtectedRoute = ({ children }) => {
const isAuthenticated = useAuthStore((state) => state.isAuthenticated);
const hasToken = !!localStorage.getItem(LANGFLOW_AUTO_LOGIN_OPTION);
const logout = useAuthStore((state) => state.logout);
const cookies = new Cookies();
const refreshToken = cookies.get("refresh_token");
const { mutate: mutateRefresh } = useRefreshAccessToken();
const { mutate: mutationLogout } = useLogout();
useEffect(() => {
const envRefreshTime = LANGFLOW_ACCESS_TOKEN_EXPIRE_SECONDS_ENV;
@ -38,7 +41,7 @@ export const ProtectedRoute = ({ children }) => {
}, [isAuthenticated]);
if (!isAuthenticated && hasToken) {
logout();
mutationLogout();
} else {
return children;
}

View file

@ -1,18 +1,16 @@
import { CustomNavigate } from "@/customization/components/custom-navigate";
import useAuthStore from "@/stores/authStore";
import { Navigate } from "react-router-dom";
export const ProtectedLoginRoute = ({ children }) => {
const autoLogin = useAuthStore((state) => state.autoLogin);
const isAuthenticated = useAuthStore((state) => state.isAuthenticated);
if (autoLogin === true) {
window.location.replace("/");
return <Navigate to="/" replace />;
return <CustomNavigate to="/" replace />;
}
if (isAuthenticated) {
window.location.replace("/");
return <Navigate to="/" replace />;
return <CustomNavigate to="/" replace />;
}
return children;

View file

@ -1,19 +1,18 @@
import FeatureFlags from "@/../feature-config.json";
import { CustomNavigate } from "@/customization/components/custom-navigate";
import { ENABLE_PROFILE_ICONS } from "@/customization/feature-flags";
import useAuthStore from "@/stores/authStore";
import { useStoreStore } from "@/stores/storeStore";
import { Navigate } from "react-router-dom";
export const AuthSettingsGuard = ({ children }) => {
const autoLogin = useAuthStore((state) => state.autoLogin);
const hasStore = useStoreStore((state) => state.hasStore);
// Hides the General settings if there is nothing to show
const showGeneralSettings =
FeatureFlags.ENABLE_PROFILE_ICONS || hasStore || !autoLogin;
const showGeneralSettings = ENABLE_PROFILE_ICONS || hasStore || !autoLogin;
if (showGeneralSettings) {
return children;
} else {
return <Navigate replace to="global-variables" />;
return <CustomNavigate replace to="global-variables" />;
}
};

View file

@ -1,8 +1,8 @@
import { useCustomNavigate } from "@/customization/hooks/use-custom-navigate";
import { useEffect } from "react";
import { useNavigate } from "react-router-dom";
export const CatchAllRoute = () => {
const navigate = useNavigate();
const navigate = useCustomNavigate();
// Redirect to the root ("/") when the catch-all route is matched
useEffect(() => {

View file

@ -1,4 +1,4 @@
import FeatureFlags from "@/../feature-config.json";
import { ENABLE_API } from "@/customization/feature-flags";
import { Transition } from "@headlessui/react";
import { useMemo, useState } from "react";
import { useHotkeys } from "react-hotkeys-hook";
@ -138,7 +138,7 @@ export default function FlowToolbar(): JSX.Element {
<div>
<Separator orientation="vertical" />
</div>
{FeatureFlags.ENABLE_API && (
{ENABLE_API && (
<>
<div className="flex cursor-pointer items-center gap-2">
{currentFlow && currentFlow.data && (

View file

@ -4,6 +4,7 @@ import {
usePostUploadFolders,
} from "@/controllers/API/queries/folders";
import { useGetDownloadFolders } from "@/controllers/API/queries/folders/use-get-download-folders";
import { ENABLE_CUSTOM_PARAM } from "@/customization/feature-flags";
import { createFileUpload } from "@/helpers/create-file-upload";
import { getObjectsFromFilelist } from "@/helpers/get-objects-from-filelist";
import useUploadFlow from "@/hooks/flows/use-upload-flow";
@ -44,7 +45,8 @@ const SideBarFoldersButtonsComponent = ({
folders.map((obj) => ({ name: obj.name, edit: false })) ?? [],
);
const currentFolder = pathname.split("/");
const urlWithoutPath = pathname.split("/").length < 4;
const urlWithoutPath =
pathname.split("/").length < (ENABLE_CUSTOM_PARAM ? 5 : 4);
const myCollectionId = useFolderStore((state) => state.myCollectionId);
const folderIdDragging = useFolderStore((state) => state.folderIdDragging);

View file

@ -7,12 +7,12 @@ import {
DropdownMenuTrigger,
} from "../../../ui/dropdown-menu";
import { useCustomNavigate } from "@/customization/hooks/use-custom-navigate";
import useAddFlow from "@/hooks/flows/use-add-flow";
import useSaveFlow from "@/hooks/flows/use-save-flow";
import useUploadFlow from "@/hooks/flows/use-upload-flow";
import { customStringify } from "@/utils/reactflowUtils";
import { useHotkeys } from "react-hotkeys-hook";
import { useNavigate } from "react-router-dom";
import { UPLOAD_ERROR_ALERT } from "../../../../constants/alerts_constants";
import { SAVED_HOVER } from "../../../../constants/constants";
import ExportModal from "../../../../modals/exportModal";
@ -40,7 +40,7 @@ export const MenuBar = ({}: {}): JSX.Element => {
const [openSettings, setOpenSettings] = useState(false);
const [openLogs, setOpenLogs] = useState(false);
const uploadFlow = useUploadFlow();
const navigate = useNavigate();
const navigate = useCustomNavigate();
const isBuilding = useFlowStore((state) => state.isBuilding);
const getTypes = useTypesStore((state) => state.getTypes);
const saveFlow = useSaveFlow();

View file

@ -1,7 +1,7 @@
import { useContext } from "react";
import { FaDiscord, FaGithub } from "react-icons/fa";
import { RiTwitterXFill } from "react-icons/ri";
import { Link, useLocation, useNavigate } from "react-router-dom";
import { useLocation } from "react-router-dom";
import AlertDropdown from "../../alerts/alertDropDown";
import {
BASE_URL_API,
@ -10,8 +10,14 @@ import {
} from "../../constants/constants";
import { AuthContext } from "../../contexts/authContext";
import FeatureFlags from "@/../feature-config.json";
import { useLogout } from "@/controllers/API/queries/auth";
import { CustomLink } from "@/customization/components/custom-link";
import {
ENABLE_DARK_MODE,
ENABLE_PROFILE_ICONS,
ENABLE_SOCIAL_LINKS,
} from "@/customization/feature-flags";
import { useCustomNavigate } from "@/customization/hooks/use-custom-navigate";
import useAuthStore from "@/stores/authStore";
import useAlertStore from "../../stores/alertStore";
import { useDarkStore } from "../../stores/darkStore";
@ -38,9 +44,8 @@ export default function Header(): JSX.Element {
const autoLogin = useAuthStore((state) => state.autoLogin);
const { mutate: mutationLogout } = useLogout();
const logout = useAuthStore((state) => state.logout);
const navigate = useNavigate();
const navigate = useCustomNavigate();
const hasStore = useStoreStore((state) => state.hasStore);
const dark = useDarkStore((state) => state.dark);
@ -65,22 +70,15 @@ export default function Header(): JSX.Element {
);
const handleLogout = () => {
mutationLogout(undefined, {
onSuccess: () => {
logout();
},
onError: (error) => {
console.error(error);
},
});
mutationLogout();
};
return (
<div className="header-arrangement relative">
<div className="header-start-display">
<Link to="/all" className="cursor-pointer">
<CustomLink to="/all" className="cursor-pointer">
<span className="ml-4 text-2xl"></span>
</Link>
</CustomLink>
{showArrowReturnIcon && (
<Button
unstyled
@ -96,12 +94,12 @@ export default function Header(): JSX.Element {
</div>
<div className="flex items-center xl:absolute xl:left-1/2 xl:-translate-x-1/2">
<Link to="/all">
<CustomLink to="/all">
<Button
className="gap-2"
variant={
location.pathname === "/all" ||
location.pathname === "/components"
location.pathname.includes("/all") ||
location.pathname.includes("/components")
? "primary"
: "secondary"
}
@ -110,25 +108,27 @@ export default function Header(): JSX.Element {
<IconComponent name="Home" className="h-4 w-4" />
<div className="hidden flex-1 lg:block">{USER_PROJECTS_HEADER}</div>
</Button>
</Link>
</CustomLink>
{hasStore && (
<Link to="/store">
<CustomLink to="/store">
<Button
className="gap-2"
variant={location.pathname === "/store" ? "primary" : "secondary"}
variant={
location.pathname.includes("/store") ? "primary" : "secondary"
}
size="sm"
data-testid="button-store"
>
<IconComponent name="Store" className="h-4 w-4" />
<div className="hidden flex-1 lg:block">Store</div>
</Button>
</Link>
</CustomLink>
)}
</div>
<div className="header-end-division">
<div className="header-end-display">
{FeatureFlags.ENABLE_SOCIAL_LINKS && (
{ENABLE_SOCIAL_LINKS && (
<>
<a
href="https://github.com/langflow-ai/langflow"
@ -160,7 +160,7 @@ export default function Header(): JSX.Element {
<Separator orientation="vertical" />
</>
)}
{FeatureFlags.ENABLE_DARK_MODE && (
{ENABLE_DARK_MODE && (
<button
className="extra-side-bar-save-disable"
onClick={() => {
@ -202,7 +202,7 @@ export default function Header(): JSX.Element {
data-testid="user-profile-settings"
className="shrink-0"
>
{FeatureFlags.ENABLE_PROFILE_ICONS ? (
{ENABLE_PROFILE_ICONS ? (
<img
src={profileImageUrl}
className="h-7 w-7 shrink-0 focus-visible:outline-0"

View file

@ -1,4 +1,4 @@
import { Link } from "react-router-dom";
import { CustomLink } from "@/customization/components/custom-link";
import { cn } from "../../../../utils/utils";
import { buttonVariants } from "../../../ui/button";
@ -18,13 +18,13 @@ const SideBarButtonsComponent = ({
return (
<div className="flex gap-2 overflow-auto lg:h-[70vh] lg:flex-col">
{items.map((item) => (
<Link to={item.href!}>
<CustomLink to={item.href!}>
<div
key={item.title}
data-testid={`sidebar-nav-${item.title}`}
className={cn(
buttonVariants({ variant: "ghost" }),
pathname === item.href
item.href && pathname.endsWith(item.href)
? "border border-border bg-muted hover:bg-muted"
: "border border-transparent hover:border-border hover:bg-transparent",
"flex w-full shrink-0 justify-start gap-4",
@ -35,7 +35,7 @@ const SideBarButtonsComponent = ({
{item.title}
</span>
</div>
</Link>
</CustomLink>
))}
</div>
);

View file

@ -1,11 +1,11 @@
import { Navigate } from "react-router-dom";
import { CustomNavigate } from "@/customization/components/custom-navigate";
import { useStoreStore } from "../../stores/storeStore";
export const StoreGuard = ({ children }) => {
const hasStore = useStoreStore((state) => state.hasStore);
if (!hasStore) {
return <Navigate to="/all" replace />;
return <CustomNavigate to="/all" replace />;
}
return children;

View file

@ -1,5 +1,6 @@
// src/constants/constants.ts
import custom from "../customization/config-constants";
import { languageMap } from "../types/components";
/**
@ -567,9 +568,7 @@ export const ADMIN_HEADER_TITLE = "Admin Page";
export const ADMIN_HEADER_DESCRIPTION =
"Navigate through this section to efficiently oversee all application users. From here, you can seamlessly manage user accounts.";
export const BASE_URL_API = "/api/v1/";
export const BACKEND_URL = process.env.BACKEND_URL || "http://localhost:7860/";
export const BASE_URL_API = custom.BASE_URL_API || "/api/v1/";
/**
* URLs excluded from error retries.
@ -580,7 +579,7 @@ export const URL_EXCLUDED_FROM_ERROR_RETRIES = [
`${BASE_URL_API}validate/code`,
`${BASE_URL_API}custom_component`,
`${BASE_URL_API}validate/prompt`,
`http://localhost:7860/login`,
`${BASE_URL_API}/login`,
`${BASE_URL_API}api_key/store`,
];

View file

@ -1,3 +1,4 @@
import { CustomWrapper } from "@/customization/custom-wrapper";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ReactNode } from "react";
import { ReactFlowProvider } from "reactflow";
@ -10,16 +11,18 @@ export default function ContextWrapper({ children }: { children: ReactNode }) {
//element to wrap all context
return (
<>
<QueryClientProvider client={queryClient}>
<AuthProvider>
<TooltipProvider skipDelayDuration={0}>
<ReactFlowProvider>
<ApiInterceptor />
{children}
</ReactFlowProvider>
</TooltipProvider>
</AuthProvider>
</QueryClientProvider>
<CustomWrapper>
<QueryClientProvider client={queryClient}>
<AuthProvider>
<TooltipProvider skipDelayDuration={0}>
<ReactFlowProvider>
<ApiInterceptor />
{children}
</ReactFlowProvider>
</TooltipProvider>
</AuthProvider>
</QueryClientProvider>
</CustomWrapper>
</>
);
}

View file

@ -1,4 +1,5 @@
import { LANGFLOW_ACCESS_TOKEN } from "@/constants/constants";
import { useCustomApiHeaders } from "@/customization/hooks/use-custom-api-headers";
import useAuthStore from "@/stores/authStore";
import axios, { AxiosError, AxiosInstance, AxiosRequestConfig } from "axios";
import { useContext, useEffect } from "react";
@ -22,8 +23,8 @@ function ApiInterceptor() {
let { accessToken, authenticationErrorCount } = useContext(AuthContext);
const { mutate: mutationLogout } = useLogout();
const { mutate: mutationRenewAccessToken } = useRefreshAccessToken();
const logout = useAuthStore((state) => state.logout);
const isLoginPage = location.pathname.includes("login");
const customHeaders = useCustomApiHeaders();
useEffect(() => {
const interceptor = api.interceptors.response.use(
@ -102,6 +103,10 @@ function ApiInterceptor() {
config.headers["Authorization"] = `Bearer ${accessToken}`;
}
for (const [key, value] of Object.entries(customHeaders)) {
config.headers[key] = value;
}
return {
...config,
signal: controller.signal,
@ -117,7 +122,7 @@ function ApiInterceptor() {
api.interceptors.response.eject(interceptor);
api.interceptors.request.eject(requestInterceptor);
};
}, [accessToken, setErrorData]);
}, [accessToken, setErrorData, customHeaders]);
function checkErrorCount() {
if (isLoginPage) return;
@ -126,14 +131,7 @@ function ApiInterceptor() {
if (authenticationErrorCount > 3) {
authenticationErrorCount = 0;
mutationLogout(undefined, {
onSuccess: () => {
logout();
},
onError: (error) => {
console.error(error);
},
});
mutationLogout();
return false;
}
@ -142,6 +140,11 @@ function ApiInterceptor() {
async function tryToRenewAccessToken(error: AxiosError) {
if (isLoginPage) return;
if (error.config?.headers) {
for (const [key, value] of Object.entries(customHeaders)) {
error.config.headers[key] = value;
}
}
mutationRenewAccessToken(
{},
{
@ -152,14 +155,7 @@ function ApiInterceptor() {
},
onError: (error) => {
console.error(error);
mutationLogout(undefined, {
onSuccess: () => {
logout();
},
onError: (error) => {
console.error(error);
},
});
mutationLogout();
return Promise.reject("Authentication error");
},
},

View file

@ -8,9 +8,10 @@ interface IDeleteApiKey {
}
// add types for error handling and success
export const useDeleteApiKey: useMutationFunctionType<IDeleteApiKey> = (
options,
) => {
export const useDeleteApiKey: useMutationFunctionType<
undefined,
IDeleteApiKey
> = (options) => {
const { mutate } = UseRequestProcessor();
const deleteApiKeyFn = async (payload: IDeleteApiKey): Promise<any> => {

View file

@ -6,6 +6,7 @@ import { useQueryFunctionType, Users } from "../../../../types/api";
import { api } from "../../api";
import { getURL } from "../../helpers/constants";
import { UseRequestProcessor } from "../../services/request-processor";
import { useLogout } from "./use-post-logout";
export interface AutoLoginResponse {
frontend_timeout: number;
@ -21,8 +22,8 @@ export const useGetAutoLogin: useQueryFunctionType<undefined, undefined> = (
const { login, setUserData, getUser } = useContext(AuthContext);
const setAutoLogin = useAuthStore((state) => state.setAutoLogin);
const isAuthenticated = useAuthStore((state) => state.isAuthenticated);
const logout = useAuthStore((state) => state.logout);
const isLoginPage = location.pathname.includes("login");
const { mutate: mutationLogout } = useLogout();
async function getAutoLoginFn(): Promise<null> {
try {
@ -40,7 +41,7 @@ export const useGetAutoLogin: useQueryFunctionType<undefined, undefined> = (
setAutoLogin(false);
if (!isLoginPage) {
if (!isAuthenticated) {
await logout();
mutationLogout();
throw new Error("Unauthorized");
} else {
getUser();

View file

@ -1,6 +1,7 @@
import useAuthStore from "@/stores/authStore";
import { useMutationFunctionType } from "@/types/api";
import { useCustomNavigate } from "@/customization/hooks/use-custom-navigate";
import { api } from "../../api";
import { getURL } from "../../helpers/constants";
import { UseRequestProcessor } from "../../services/request-processor";
@ -9,6 +10,7 @@ export const useLogout: useMutationFunctionType<undefined, void> = (
options?,
) => {
const { mutate } = UseRequestProcessor();
const navigate = useCustomNavigate();
const logout = useAuthStore((state) => state.logout);
async function logoutUser(): Promise<any> {
@ -23,6 +25,7 @@ export const useLogout: useMutationFunctionType<undefined, void> = (
const mutation = mutate(["useLogout"], logoutUser, {
onSuccess: () => {
logout();
navigate("/login");
},
onError: (error) => {
console.error(error);

View file

@ -12,7 +12,7 @@ export interface ConfigResponse {
health_check_max_retries: number;
}
export const useGetConfig: useQueryFunctionType<undefined, undefined> = (
export const useGetConfig: useQueryFunctionType<undefined, ConfigResponse> = (
options,
) => {
const setAutoSaving = useFlowsManagerStore((state) => state.setAutoSaving);
@ -38,6 +38,7 @@ export const useGetConfig: useQueryFunctionType<undefined, undefined> = (
setAutoSavingInterval(data.auto_saving_interval);
setHealthCheckMaxRetries(data.health_check_max_retries);
}
return data;
};
const queryResult = query(["useGetConfig"], getConfigFn, options);

View file

@ -1,32 +0,0 @@
import axios from "axios";
import { BASE_URL_API } from "../../constants/constants";
/**
* Fetches the configuration data from the API.
* @returns {Promise<any>} A promise that resolves to the configuration data.
* @throws {Error} If there was an error fetching the configuration data.
*/
export async function fetchConfig() {
try {
const response = await axios.get(`${BASE_URL_API}config`);
return response.data;
} catch (error) {
console.error("Failed to fetch configuration:", error);
throw error;
}
}
/**
* Sets up default configurations for Axios.
* Fetches the timeout configuration and sets it as the default timeout for Axios requests.
*/
export async function setupAxiosDefaults() {
const config = await fetchConfig();
// Create Axios instance with the fetched timeout configuration
const timeoutInMilliseconds = config.frontend_timeout
? config.frontend_timeout * 1000
: 30000;
axios.defaults.baseURL = "";
axios.defaults.timeout = timeoutInMilliseconds;
}

View file

@ -0,0 +1,3 @@
export function CustomHeader() {
return <></>;
}

View file

@ -0,0 +1,11 @@
import { Link, LinkProps, useParams } from "react-router-dom";
import { ENABLE_CUSTOM_PARAM } from "../feature-flags";
export function CustomLink({ to, ...props }: LinkProps) {
const { customParam } = useParams();
const newLocation =
ENABLE_CUSTOM_PARAM && to[0] === "/" ? `/${customParam}${to}` : to;
return <Link to={newLocation} {...props} />;
}

View file

@ -0,0 +1,10 @@
import { Navigate, NavigateProps, useParams } from "react-router-dom";
import { ENABLE_CUSTOM_PARAM } from "../feature-flags";
export function CustomNavigate({ to, ...props }: NavigateProps) {
const { customParam } = useParams();
const newLocation =
ENABLE_CUSTOM_PARAM && to[0] === "/" ? `/${customParam}${to}` : to;
return <Navigate to={newLocation} {...props} />;
}

View file

@ -0,0 +1,13 @@
export const BASENAME = "";
export const PORT = 3000;
export const PROXY_TARGET = "http://127.0.0.1:7860";
export const API_ROUTES = ["^/api/v1/", "/health"];
export const BASE_URL_API = "/api/v1/";
export default {
BASENAME,
PORT,
PROXY_TARGET,
API_ROUTES,
BASE_URL_API,
};

View file

@ -0,0 +1,3 @@
export function CustomWrapper({ children }) {
return children;
}

View file

@ -0,0 +1,8 @@
export const ENABLE_DARK_MODE = true;
export const ENABLE_API = true;
export const ENABLE_LANGFLOW_STORE = true;
export const ENABLE_PROFILE_ICONS = true;
export const ENABLE_SOCIAL_LINKS = true;
export const ENABLE_BRANDING = true;
export const ENABLE_MVPS = false;
export const ENABLE_CUSTOM_PARAM = false;

View file

@ -0,0 +1,5 @@
export function useCustomApiHeaders() {
const customHeaders = {};
return customHeaders;
}

View file

@ -0,0 +1,27 @@
import {
NavigateFunction,
NavigateOptions,
To,
useNavigate,
useParams,
} from "react-router-dom";
import { ENABLE_CUSTOM_PARAM } from "../feature-flags";
export function useCustomNavigate(): NavigateFunction {
const domNavigate = useNavigate();
const { customParam } = useParams();
function navigate(to: To | number, options?: NavigateOptions) {
if (typeof to === "number") {
domNavigate(to);
} else {
domNavigate(
ENABLE_CUSTOM_PARAM && to[0] === "/" ? `/${customParam}${to}` : to,
options,
);
}
}
return navigate;
}

View file

@ -0,0 +1,20 @@
import { UseRequestProcessor } from "@/controllers/API/services/request-processor";
import { useQueryFunctionType } from "@/types/api";
export const usePrimaryLoading: useQueryFunctionType<undefined, null> = (
options,
) => {
const { query } = UseRequestProcessor();
const getPrimaryLoadingFn = async () => {
return null;
};
const queryResult = query(
["usePrimaryLoading"],
getPrimaryLoadingFn,
options,
);
return queryResult;
};

View file

@ -1,8 +1,7 @@
import { useGetDownloadFileMutation } from "@/controllers/API/queries/files";
import { useState } from "react";
import { ForwardedIconComponent } from "../../../../../components/genericIconComponent";
import { BACKEND_URL, BASE_URL_API } from "../../../../../constants/constants";
import useFlowsManagerStore from "../../../../../stores/flowsManagerStore";
import { BASE_URL_API } from "../../../../../constants/constants";
import { fileCardPropsType } from "../../../../../types/components";
import formatFileName from "../filePreviewChat/utils/format-file-name";
import DownloadButton from "./components/downloadButton/downloadButton";

View file

@ -1,5 +1,6 @@
import { useCustomNavigate } from "@/customization/hooks/use-custom-navigate";
import useAddFlow from "@/hooks/flows/use-add-flow";
import { useNavigate, useParams } from "react-router-dom";
import { useParams } from "react-router-dom";
import {
Card,
CardContent,
@ -9,7 +10,7 @@ import {
export default function NewFlowCardComponent() {
const addFlow = useAddFlow();
const navigate = useNavigate();
const navigate = useCustomNavigate();
const { folderId } = useParams();
return (

View file

@ -1,5 +1,5 @@
/// <reference types="vite-plugin-svgr/client" />
import { useNavigate, useParams } from "react-router-dom";
import { useParams } from "react-router-dom";
import BlogPost from "../../../../assets/undraw_blog_post_re_fy5x.svg?react";
import ChatBot from "../../../../assets/undraw_chat_bot_re_e2gj.svg?react";
import PromptChaining from "../../../../assets/undraw_cloud_docs_re_xjht.svg?react";
@ -10,6 +10,7 @@ import APIRequest from "../../../../assets/undraw_real_time_analytics_re_yliv.sv
import BasicPrompt from "../../../../assets/undraw_short_bio_re_fmx0.svg?react";
import TransferFiles from "../../../../assets/undraw_transfer_files_re_a2a9.svg?react";
import { useCustomNavigate } from "@/customization/hooks/use-custom-navigate";
import useAddFlow from "@/hooks/flows/use-add-flow";
import {
Card,
@ -25,7 +26,7 @@ export default function UndrawCardComponent({
flow,
}: UndrawCardComponentProps): JSX.Element {
const addFlow = useAddFlow();
const navigate = useNavigate();
const navigate = useCustomNavigate();
const { folderId } = useParams();
const myCollectionId = useFolderStore((state) => state.myCollectionId);

View file

@ -1,6 +1,7 @@
import { useGetAutoLogin } from "@/controllers/API/queries/auth";
import { useGetConfig } from "@/controllers/API/queries/config/use-get-config";
import { useGetVersionQuery } from "@/controllers/API/queries/version";
import { usePrimaryLoading } from "@/customization/hooks/use-primary-loading";
import { useDarkStore } from "@/stores/darkStore";
import useFlowsManagerStore from "@/stores/flowsManagerStore";
import { useEffect } from "react";
@ -12,7 +13,8 @@ export function AppInitPage() {
const refreshStars = useDarkStore((state) => state.refreshStars);
const isLoading = useFlowsManagerStore((state) => state.isLoading);
const { isFetched } = useGetAutoLogin();
const { isFetched: isLoaded } = usePrimaryLoading();
const { isFetched } = useGetAutoLogin({ enabled: isLoaded });
useGetVersionQuery({ enabled: isFetched });
useGetConfig({ enabled: isFetched });

View file

@ -9,6 +9,7 @@ import {
TIMEOUT_ERROR_MESSAGE,
} from "@/constants/constants";
import { useGetHealthQuery } from "@/controllers/API/queries/health";
import { CustomHeader } from "@/customization/components/custom-header";
import useFlowsManagerStore from "@/stores/flowsManagerStore";
import { useUtilityStore } from "@/stores/utilityStore";
import { AxiosError } from "axios";
@ -96,6 +97,7 @@ export function AppWrapperPage() {
return (
<div className="flex h-full flex-col">
<CustomHeader />
<ErrorBoundary
onReset={() => {
// any reset function
@ -107,7 +109,6 @@ export function AppWrapperPage() {
<Outlet />
</>
</ErrorBoundary>
<div></div>
<div className="app-div">
<AlertDisplayArea />
</div>

View file

@ -1,4 +1,4 @@
import FeatureFlags from "@/../feature-config.json";
import { ENABLE_MVPS } from "@/customization/feature-flags";
import { useStoreStore } from "@/stores/storeStore";
import { cloneDeep } from "lodash";
import { useEffect, useState } from "react";
@ -193,10 +193,10 @@ export default function ExtraSidebar(): JSX.Element {
// Set search input state
setSearch(event.target.value);
}}
autocomplete="off"
readonly="readonly"
autoComplete="off"
readOnly
onClick={() =>
document.getElementById("search").removeAttribute("readonly")
document?.getElementById("search")?.removeAttribute("readonly")
}
/>
<div
@ -298,7 +298,7 @@ export default function ExtraSidebar(): JSX.Element {
</a>
)}
</ParentDisclosureComponent>
{FeatureFlags.ENABLE_MVPS && (
{ENABLE_MVPS && (
<>
<Separator />

View file

@ -1,12 +1,13 @@
import FeatureFlags from "@/../feature-config.json";
import { useGetRefreshFlows } from "@/controllers/API/queries/flows/use-get-refresh-flows";
import { useGetGlobalVariables } from "@/controllers/API/queries/variables";
import { ENABLE_BRANDING } from "@/customization/feature-flags";
import { useCustomNavigate } from "@/customization/hooks/use-custom-navigate";
import useSaveFlow from "@/hooks/flows/use-save-flow";
import { SaveChangesModal } from "@/modals/saveChangesModal";
import { useTypesStore } from "@/stores/typesStore";
import { customStringify } from "@/utils/reactflowUtils";
import { useEffect } from "react";
import { useBlocker, useNavigate, useParams } from "react-router-dom";
import { useBlocker, useParams } from "react-router-dom";
import FlowToolbar from "../../components/chatComponent";
import { useDarkStore } from "../../stores/darkStore";
import useFlowStore from "../../stores/flowStore";
@ -27,7 +28,7 @@ export default function FlowPage({ view }: { view?: boolean }): JSX.Element {
const version = useDarkStore((state) => state.version);
const setOnFlowPage = useFlowStore((state) => state.setOnFlowPage);
const { id } = useParams();
const navigate = useNavigate();
const navigate = useCustomNavigate();
useGetGlobalVariables();
const saveFlow = useSaveFlow();
@ -106,7 +107,7 @@ export default function FlowPage({ view }: { view?: boolean }): JSX.Element {
</main>
</div>
)}
{FeatureFlags.ENABLE_BRANDING && version && (
{ENABLE_BRANDING && version && (
<a
target={"_blank"}
href="https://medium.com/logspace/langflow-datastax-better-together-1b7462cebc4d"

View file

@ -1,7 +1,7 @@
import { useLoginUser } from "@/controllers/API/queries/auth";
import { CustomLink } from "@/customization/components/custom-link";
import * as Form from "@radix-ui/react-form";
import { useContext, useState } from "react";
import { Link } from "react-router-dom";
import InputComponent from "../../components/inputComponent";
import { Button } from "../../components/ui/button";
import { Input } from "../../components/ui/input";
@ -124,11 +124,11 @@ export default function LoginPage(): JSX.Element {
</Form.Submit>
</div>
<div className="w-full">
<Link to="/signup">
<CustomLink to="/signup">
<Button className="w-full" variant="outline" type="button">
Don't have an account?&nbsp;<b>Sign Up</b>
</Button>
</Link>
</CustomLink>
</div>
</div>
</div>

View file

@ -1,9 +1,8 @@
import { Link, useNavigate, useParams } from "react-router-dom";
import { useCustomNavigate } from "@/customization/hooks/use-custom-navigate";
import { useParams } from "react-router-dom";
import CollectionCardComponent from "../../../../../../components/cardComponent";
import IconComponent from "../../../../../../components/genericIconComponent";
import { Button } from "../../../../../../components/ui/button";
const CollectionCard = ({ item, type, isLoading, control }) => {
const navigate = useNavigate();
const navigate = useCustomNavigate();
const isComponent = item.is_component ?? false;
const editFlowButtonTestId = `edit-flow-button-${item.id}`;

View file

@ -1,5 +1,5 @@
import { useCustomNavigate } from "@/customization/hooks/use-custom-navigate";
import { useEffect } from "react";
import { useNavigate } from "react-router-dom";
type TabsSearchComponentProps = {
tabsOptions: string[];
@ -14,23 +14,23 @@ const TabsSearchComponent = ({
loading,
tabActive,
}: TabsSearchComponentProps) => {
const navigate = useNavigate();
const navigate = useCustomNavigate();
const changeLocation = (tabOption) => {
const location = window.location.pathname;
let newLocation = "";
switch (tabOption) {
case "Flows":
newLocation = location.replace(/components|all/, "flows");
newLocation = location.replace(/.*\/(?:all|components)/, "/flows");
break;
case "Components":
newLocation = location.replace(/flows|all/, "components");
newLocation = location.replace(/.*\/(?:flows|all)/, "/components");
break;
default:
newLocation = location.replace(/flows|components/, "all");
newLocation = location.replace(/.*\/(?:flows|components)/, "/all");
break;
}
console.log(newLocation);
navigate(newLocation);
};

View file

@ -1,8 +1,9 @@
import FolderSidebarNav from "@/components/folderSidebarComponent";
import { useDeleteFolders } from "@/controllers/API/queries/folders";
import { useCustomNavigate } from "@/customization/hooks/use-custom-navigate";
import useAlertStore from "@/stores/alertStore";
import { useState } from "react";
import { Outlet, useLocation, useNavigate } from "react-router-dom";
import { Outlet, useLocation } from "react-router-dom";
import DropdownButton from "../../../../components/dropdownButtonComponent";
import PageLayout from "../../../../components/pageLayout";
import {
@ -18,9 +19,9 @@ export default function HomePage(): JSX.Element {
const pathname = location.pathname;
const [openModal, setOpenModal] = useState(false);
const [openDeleteFolderModal, setOpenDeleteFolderModal] = useState(false);
const is_component = pathname === "/components";
const is_component = pathname.includes("/components");
const setFolderToEdit = useFolderStore((state) => state.setFolderToEdit);
const navigate = useNavigate();
const navigate = useCustomNavigate();
const setSuccessData = useAlertStore((state) => state.setSuccessData);
const setErrorData = useAlertStore((state) => state.setErrorData);

View file

@ -1,9 +1,10 @@
import { useGetRefreshFlows } from "@/controllers/API/queries/flows/use-get-refresh-flows";
import { useGetGlobalVariables } from "@/controllers/API/queries/variables";
import { useCustomNavigate } from "@/customization/hooks/use-custom-navigate";
import { useStoreStore } from "@/stores/storeStore";
import { useTypesStore } from "@/stores/typesStore";
import { useEffect } from "react";
import { useNavigate, useParams } from "react-router-dom";
import { useParams } from "react-router-dom";
import { getComponent } from "../../controllers/API";
import IOModal from "../../modals/IOModal";
import useFlowsManagerStore from "../../stores/flowsManagerStore";
@ -22,7 +23,7 @@ export default function PlaygroundPage() {
return newFlow;
}
const navigate = useNavigate();
const navigate = useCustomNavigate();
useGetGlobalVariables();
const currentFlowId = useFlowsManagerStore((state) => state.currentFlowId);

View file

@ -1,4 +1,4 @@
import FeatureFlags from "@/../feature-config.json";
import { ENABLE_PROFILE_ICONS } from "@/customization/feature-flags";
import useAuthStore from "@/stores/authStore";
import { useStoreStore } from "@/stores/storeStore";
import { Outlet } from "react-router-dom";
@ -11,8 +11,7 @@ export default function SettingsPage(): JSX.Element {
const hasStore = useStoreStore((state) => state.hasStore);
// Hides the General settings if there is nothing to show
const showGeneralSettings =
FeatureFlags.ENABLE_PROFILE_ICONS || hasStore || !autoLogin;
const showGeneralSettings = ENABLE_PROFILE_ICONS || hasStore || !autoLogin;
const sidebarNavItems: {
href?: string;

View file

@ -1,4 +1,3 @@
import FeatureFlags from "@/../feature-config.json";
import {
EDIT_PASSWORD_ALERT_LIST,
EDIT_PASSWORD_ERROR_ALERT,
@ -11,6 +10,7 @@ import {
useUpdateUser,
} from "@/controllers/API/queries/auth";
import { useGetProfilePicturesQuery } from "@/controllers/API/queries/files";
import { ENABLE_PROFILE_ICONS } from "@/customization/feature-flags";
import useAuthStore from "@/stores/authStore";
import { cloneDeep } from "lodash";
import { useContext, useState } from "react";
@ -146,7 +146,7 @@ export const GeneralPage = () => {
<GeneralPageHeaderComponent />
<div className="grid gap-6">
{FeatureFlags.ENABLE_PROFILE_ICONS && (
{ENABLE_PROFILE_ICONS && (
<ProfilePictureFormComponent
profilePicture={profilePicture}
handleInput={handleInput}

View file

@ -1,7 +1,8 @@
import { useAddUser } from "@/controllers/API/queries/auth";
import { CustomLink } from "@/customization/components/custom-link";
import { useCustomNavigate } from "@/customization/hooks/use-custom-navigate";
import * as Form from "@radix-ui/react-form";
import { FormEvent, useEffect, useState } from "react";
import { Link, useNavigate } from "react-router-dom";
import InputComponent from "../../components/inputComponent";
import { Button } from "../../components/ui/button";
import { Input } from "../../components/ui/input";
@ -26,7 +27,7 @@ export default function SignUp(): JSX.Element {
const { password, cnfPassword, username } = inputState;
const setSuccessData = useAlertStore((state) => state.setSuccessData);
const setErrorData = useAlertStore((state) => state.setErrorData);
const navigate = useNavigate();
const navigate = useCustomNavigate();
const { mutate: mutateAddUser } = useAddUser();
@ -184,11 +185,11 @@ export default function SignUp(): JSX.Element {
</Form.Submit>
</div>
<div className="w-full">
<Link to="/login">
<CustomLink to="/login">
<Button className="w-full" variant="outline">
Already have an account?&nbsp;<b>Sign in</b>
</Button>
</Link>
</CustomLink>
</div>
</div>
</div>

View file

@ -1,6 +1,5 @@
import { uniqueId } from "lodash";
import { useContext, useEffect, useState } from "react";
import CollectionCardComponent from "../../components/cardComponent";
import IconComponent from "../../components/genericIconComponent";
import PageLayout from "../../components/pageLayout";
import ShadTooltip from "../../components/shadTooltipComponent";
@ -9,7 +8,9 @@ import { Button } from "../../components/ui/button";
import StoreCardComponent from "@/components/storeCardComponent";
import { useGetTagsQuery } from "@/controllers/API/queries/store";
import { Link, useNavigate, useParams } from "react-router-dom";
import { CustomLink } from "@/customization/components/custom-link";
import { useCustomNavigate } from "@/customization/hooks/use-custom-navigate";
import { useParams } from "react-router-dom";
import PaginatorComponent from "../../components/paginatorComponent";
import { TagsSelector } from "../../components/tagsSelectorComponent";
import { Badge } from "../../components/ui/badge";
@ -62,7 +63,7 @@ export default function StorePage(): JSX.Element {
const [selectFilter, setSelectFilter] = useState("all");
const { isFetching, data } = useGetTagsQuery();
const navigate = useNavigate();
const navigate = useCustomNavigate();
useEffect(() => {
if (!loadingApiKey) {
@ -289,9 +290,9 @@ export default function StorePage(): JSX.Element {
size="sq"
className="gap-2 bg-beta-foreground text-background hover:bg-beta-foreground"
>
<Link to={"/store"} className="cursor-pointer">
<CustomLink to={"/store"} className="cursor-pointer">
<IconComponent name="X" className="h-4 w-4" />
</Link>
</CustomLink>
{id}
</Badge>
)}

View file

@ -1,8 +1,9 @@
import { useGetRefreshFlows } from "@/controllers/API/queries/flows/use-get-refresh-flows";
import { useGetGlobalVariables } from "@/controllers/API/queries/variables";
import { useCustomNavigate } from "@/customization/hooks/use-custom-navigate";
import { useTypesStore } from "@/stores/typesStore";
import { useEffect } from "react";
import { useNavigate, useParams } from "react-router-dom";
import { useParams } from "react-router-dom";
import useFlowsManagerStore from "../../stores/flowsManagerStore";
import Page from "../FlowPage/components/PageComponent";
@ -10,7 +11,7 @@ export default function ViewPage() {
const setCurrentFlow = useFlowsManagerStore((state) => state.setCurrentFlow);
const { id } = useParams();
const navigate = useNavigate();
const navigate = useCustomNavigate();
useGetGlobalVariables();
const flows = useFlowsManagerStore((state) => state.flows);

View file

@ -2,7 +2,6 @@ import { lazy } from "react";
import {
createBrowserRouter,
createRoutesFromElements,
Navigate,
Outlet,
Route,
} from "react-router-dom";
@ -12,6 +11,9 @@ import { ProtectedLoginRoute } from "./components/authLoginGuard";
import { AuthSettingsGuard } from "./components/authSettingsGuard";
import { StoreGuard } from "./components/storeGuard";
import ContextWrapper from "./contexts";
import { CustomNavigate } from "./customization/components/custom-navigate";
import { BASENAME } from "./customization/config-constants";
import { ENABLE_CUSTOM_PARAM } from "./customization/feature-flags";
import { AppInitPage } from "./pages/AppInitPage";
import { AppWrapperPage } from "./pages/AppWrapperPage";
import { DashboardWrapperPage } from "./pages/DashboardWrapperPage";
@ -38,7 +40,7 @@ const SignUp = lazy(() => import("./pages/SignUpPage"));
const router = createBrowserRouter(
createRoutesFromElements([
<Route
path="/"
path={ENABLE_CUSTOM_PARAM ? "/:customParam" : "/"}
element={
<ContextWrapper>
<Outlet />
@ -57,7 +59,7 @@ const router = createBrowserRouter(
>
<Route path="" element={<DashboardWrapperPage />}>
<Route path="" element={<HomePage />}>
<Route index element={<Navigate replace to={"all"} />} />
<Route index element={<CustomNavigate replace to={"all"} />} />
<Route
path="flows/"
element={<MyCollectionComponent key="flows" type="flow" />}
@ -93,8 +95,11 @@ const router = createBrowserRouter(
/>
</Route>
</Route>
<Route path="/settings" element={<SettingsPage />}>
<Route index element={<Navigate replace to={"general"} />} />
<Route path="settings" element={<SettingsPage />}>
<Route
index
element={<CustomNavigate replace to={"general"} />}
/>
<Route
path="global-variables"
element={<GlobalVariablesPage />}
@ -112,7 +117,7 @@ const router = createBrowserRouter(
<Route path="messages" element={<MessagesPage />} />
</Route>
<Route
path="/store"
path="store"
element={
<StoreGuard>
<StorePage />
@ -120,18 +125,18 @@ const router = createBrowserRouter(
}
/>
<Route
path="/store/:id/"
path="store/:id/"
element={
<StoreGuard>
<StorePage />
</StoreGuard>
}
/>
<Route path="/account">
<Route path="account">
<Route path="delete" element={<DeleteAccountPage />}></Route>
</Route>
<Route
path="/admin"
path="admin"
element={
<ProtectedAdminRoute>
<AdminPage />
@ -139,19 +144,19 @@ const router = createBrowserRouter(
}
/>
</Route>
<Route path="/flow/:id/">
<Route path="flow/:id/">
<Route path="" element={<DashboardWrapperPage />}>
<Route path="folder/:folderId/" element={<FlowPage />} />
<Route path="" element={<FlowPage />} />
</Route>
<Route path="view" element={<ViewPage />} />
</Route>
<Route path="/playground/:id/">
<Route path="playground/:id/">
<Route path="" element={<PlaygroundPage />} />
</Route>
</Route>
<Route
path="/login"
path="login"
element={
<ProtectedLoginRoute>
<LoginPage />
@ -159,7 +164,7 @@ const router = createBrowserRouter(
}
/>
<Route
path="/signup"
path="signup"
element={
<ProtectedLoginRoute>
<SignUp />
@ -167,18 +172,19 @@ const router = createBrowserRouter(
}
/>
<Route
path="/login/admin"
path="login/admin"
element={
<ProtectedLoginRoute>
<LoginAdminPage />
</ProtectedLoginRoute>
}
/>
<Route path="*" element={<Navigate replace to="/" />} />
</Route>
</Route>
<Route path="*" element={<CustomNavigate replace to="/" />} />
</Route>,
]),
{ basename: BASENAME || undefined },
);
export default router;

View file

@ -35,8 +35,6 @@ const useAuthStore = create<AuthStoreType>((set, get) => ({
autoLogin: false,
apiKey: null,
});
window.location.href = "/login";
},
}));

View file

@ -1,17 +1,17 @@
import FeatureFlags from "@/../feature-config.json";
import { ENABLE_LANGFLOW_STORE } from "@/customization/feature-flags";
import { create } from "zustand";
import { checkHasApiKey, checkHasStore } from "../controllers/API";
import { StoreStoreType } from "../types/zustand/store";
export const useStoreStore = create<StoreStoreType>((set) => ({
hasStore: true,
hasStore: ENABLE_LANGFLOW_STORE,
validApiKey: false,
hasApiKey: false,
loadingApiKey: true,
checkHasStore: () => {
checkHasStore().then((res) => {
set({
hasStore: FeatureFlags.ENABLE_LANGFLOW_STORE && (res?.enabled ?? false),
hasStore: ENABLE_LANGFLOW_STORE && (res?.enabled ?? false),
});
});
},

View file

@ -58,6 +58,5 @@
"tests/scheduled-end-to-end/twoEdges.spec.ts",
"tests/scheduled-end-to-end/userSettings.spec.ts",
"tests/end-to-end/store.spec.ts",
"tests/end-to-end/logs.spec.ts"
]
"tests/end-to-end/logs.spec.ts" ]
}

View file

@ -4,17 +4,24 @@ import path from "path";
import { defineConfig } from "vite";
import svgr from "vite-plugin-svgr";
import tsconfigPaths from "vite-tsconfig-paths";
import {
API_ROUTES,
BASENAME,
PORT,
PROXY_TARGET,
} from "./src/customization/config-constants";
export default defineConfig(({ mode }) => {
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
const apiRoutes = ["^/api/v1/", "/health"];
const apiRoutes = API_ROUTES || ["^/api/v1/", "/health"];
// Use environment variable to determine the target.
const target = process.env.VITE_PROXY_TARGET || "http://127.0.0.1:7860";
const target =
process.env.VITE_PROXY_TARGET || PROXY_TARGET || "http://127.0.0.1:7860";
// Use environment variable to determine the UI server port
const port = Number(process.env.VITE_PORT) || 3000;
const port = Number(process.env.VITE_PORT) || PORT || 3000;
const proxyTargets = apiRoutes.reduce((proxyObj, route) => {
proxyObj[route] = {
@ -27,6 +34,7 @@ export default defineConfig(({ mode }) => {
}, {});
return {
basename: (BASENAME || "") + "/",
build: {
outDir: "build",
},