+
setIsTweakPage(true)}
+ >
{
saveFlow(flow!);
- setSuccessData({ title: "Changes saved successfully" });
}}
>
(CONTROL_LOGIN_STATE);
const { password, username } = inputState;
+ const { login, getAuthentication, setUserData } = useContext(AuthContext);
+ const navigate = useNavigate();
+ const { setErrorData } = useContext(alertContext);
function handleInput({
target: { name, value },
}: inputHandlerEventType): void {
setInputState((prev) => ({ ...prev, [name]: value }));
}
+
+ function signIn() {
+ const user: LoginType = {
+ username: username,
+ password: password,
+ };
+ onLogin(user)
+ .then((user) => {
+ login(user.access_token, user.refresh_token);
+ getUser();
+ navigate("/");
+ })
+ .catch((error) => {
+ setErrorData({
+ title: "Error signing in",
+ list: [error["response"]["data"]["detail"]],
+ });
+ });
+ }
+
+ function getUser() {
+ if (getAuthentication()) {
+ setTimeout(() => {
+ getLoggedUser()
+ .then((user) => {
+ setUserData(user);
+ })
+ .catch((error) => {});
+ }, 1000);
+ }
+ }
+
return (
{
@@ -28,7 +67,7 @@ export default function LoginPage(): JSX.Element {
event.preventDefault();
return;
}
-
+ signIn();
const data = Object.fromEntries(new FormData(event.currentTarget));
event.preventDefault();
}}
@@ -92,7 +131,7 @@ export default function LoginPage(): JSX.Element {
-
+
Don't have an account? Sign Up
diff --git a/src/frontend/src/pages/signUpPage/index.tsx b/src/frontend/src/pages/signUpPage/index.tsx
index c81253f4d..02b725a14 100644
--- a/src/frontend/src/pages/signUpPage/index.tsx
+++ b/src/frontend/src/pages/signUpPage/index.tsx
@@ -1,11 +1,17 @@
import * as Form from "@radix-ui/react-form";
-import { FormEvent, useState } from "react";
-import { Link } from "react-router-dom";
+import { FormEvent, useContext, 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";
-import { CONTROL_INPUT_STATE } from "../../constants/constants";
import {
+ CONTROL_INPUT_STATE,
+ SIGN_UP_SUCCESS,
+} from "../../constants/constants";
+import { alertContext } from "../../contexts/alertContext";
+import { addUser } from "../../controllers/API";
+import {
+ UserInputType,
inputHandlerEventType,
signUpInputStateType,
} from "../../types/components";
@@ -15,12 +21,42 @@ export default function SignUp(): JSX.Element {
useState(CONTROL_INPUT_STATE);
const { password, cnfPassword, username } = inputState;
+ const { setErrorData, setSuccessData } = useContext(alertContext);
+ const navigate = useNavigate();
function handleInput({
target: { name, value },
}: inputHandlerEventType): void {
setInputState((prev) => ({ ...prev, [name]: value }));
}
+
+ function handleSignup(): void {
+ const { username, password } = inputState;
+ const newUser: UserInputType = {
+ username,
+ password,
+ };
+ addUser(newUser)
+ .then((user) => {
+ setSuccessData({
+ title: SIGN_UP_SUCCESS,
+ });
+ navigate("/login");
+ })
+ .catch((error) => {
+ const {
+ response: {
+ data: { detail },
+ },
+ } = error;
+ setErrorData({
+ title: "Error signing up",
+ list: [detail[0].msg],
+ });
+ return;
+ });
+ }
+
return (
) => {
@@ -120,7 +156,14 @@ export default function SignUp(): JSX.Element {
- Sign up
+ {
+ handleSignup();
+ }}
+ >
+ Sign up
+
diff --git a/src/frontend/src/routes.tsx b/src/frontend/src/routes.tsx
index 0d023e360..04e2f80f4 100644
--- a/src/frontend/src/routes.tsx
+++ b/src/frontend/src/routes.tsx
@@ -1,32 +1,116 @@
import { Route, Routes } from "react-router-dom";
+import { ProtectedAdminRoute } from "./components/authAdminGuard";
+import { ProtectedRoute } from "./components/authGuard";
+import { ProtectedLoginRoute } from "./components/authLoginGuard";
+import { CatchAllRoute } from "./components/catchAllRoutes";
import AdminPage from "./pages/AdminPage";
import LoginAdminPage from "./pages/AdminPage/LoginPage";
+import ApiKeysPage from "./pages/ApiKeysPage";
import CommunityPage from "./pages/CommunityPage";
import FlowPage from "./pages/FlowPage";
import HomePage from "./pages/MainPage";
import ViewPage from "./pages/ViewPage";
import DeleteAccountPage from "./pages/deleteAccountPage";
import LoginPage from "./pages/loginPage";
+import SignUp from "./pages/signUpPage";
const Router = () => {
return (
- } />
- } />
+
+
+
+ }
+ />
+
+
+
+ }
+ />
- } />
- } />
+
+
+
+ }
+ />
+
+
+
+ }
+ />
- } />
+
+
+
+ }
+ />
- } />
- {/* } /> */}
- } />
+
+
+
+ }
+ />
+
+
+
+ }
+ />
+
+
+
+ }
+ />
- } />
+
+
+ }
+ />
- }>
+
+
+
+ }
+ >
+
+
+
+ }
+ >
);
diff --git a/src/frontend/src/types/api/index.ts b/src/frontend/src/types/api/index.ts
index 5a5701b2d..b690d7134 100644
--- a/src/frontend/src/types/api/index.ts
+++ b/src/frontend/src/types/api/index.ts
@@ -62,3 +62,27 @@ export type UploadFileTypeAPI = {
file_path: string;
flowId: string;
};
+
+export type LoginType = {
+ grant_type?: string;
+ username: string;
+ password: string;
+ scrope?: string;
+ client_id?: string;
+ client_secret?: string;
+};
+
+export type LoginAuthType = {
+ access_token: string;
+ refresh_token: string;
+ token_type?: string;
+};
+
+export type Users = {
+ id: string;
+ username: string;
+ is_active: boolean;
+ is_superuser: boolean;
+ create_at: Date;
+ updated_at: Date;
+};
diff --git a/src/frontend/src/types/components/index.ts b/src/frontend/src/types/components/index.ts
index d4adccb37..aec1f373f 100644
--- a/src/frontend/src/types/components/index.ts
+++ b/src/frontend/src/types/components/index.ts
@@ -218,7 +218,7 @@ export type signUpInputStateType = {
export type inputHandlerEventType = {
target: {
- value: string;
+ value: string | boolean;
name: string;
};
};
@@ -261,6 +261,27 @@ export type loginInputStateType = {
password: string;
};
+export type UserInputType = {
+ username: string;
+ password: string;
+ is_active?: boolean;
+ is_superuser?: boolean;
+};
+
+export type ApiKeyType = {
+ title: string;
+ cancelText: string;
+ confirmationText: string;
+ children: ReactElement;
+ icon: string;
+ data?: any;
+ onCloseModal: () => void;
+
+};
+
+export type ApiKeyInputType = {
+ apikeyname: string;
+};
export type groupedObjType = {
family: string;
type: string;
@@ -508,3 +529,15 @@ export type validationStatusType = {
progress: number;
valid: boolean;
};
+
+export type ApiKey = {
+ id: string;
+ api_key: string;
+ name: string;
+ created_at: string;
+ last_used_at: string;
+};
+export type fetchErrorComponentType = {
+ message: string;
+ description: string;
+};
diff --git a/src/frontend/src/types/contexts/auth.ts b/src/frontend/src/types/contexts/auth.ts
index af037ecae..97ed839ce 100644
--- a/src/frontend/src/types/contexts/auth.ts
+++ b/src/frontend/src/types/contexts/auth.ts
@@ -1,16 +1,20 @@
+import { Users } from "../api";
+
export type AuthContextType = {
+ isAdmin: boolean;
+ setIsAdmin: (isAdmin: boolean) => void;
isAuthenticated: boolean;
accessToken: string | null;
+ refreshToken: string | null;
login: (accessToken: string, refreshToken: string) => void;
logout: () => void;
refreshAccessToken: (refreshToken: string) => Promise
;
- userData: userData | null;
- setUserData: (userData: userData | null) => void;
-};
-
-export type userData = {
- id: string;
- name: string;
- email: string;
- role: string;
+ userData: Users | null;
+ setUserData: (userData: Users | null) => void;
+ getAuthentication: () => boolean;
+ authenticationErrorCount: number;
+ autoLogin: boolean;
+ setAutoLogin: (autoLogin: boolean) => void;
+ stars: number;
+ setStars: (stars: number) => void;
};
diff --git a/src/frontend/src/types/typesContext/index.ts b/src/frontend/src/types/typesContext/index.ts
index 9e57822b9..a0476f922 100644
--- a/src/frontend/src/types/typesContext/index.ts
+++ b/src/frontend/src/types/typesContext/index.ts
@@ -16,6 +16,8 @@ export type typesContextType = {
setTemplates: (newState: {}) => void;
data: APIDataType;
setData: (newState: {}) => void;
+ fetchError: boolean;
+ setFetchError: (newState: boolean) => void;
};
export type alertContextType = {
@@ -39,6 +41,8 @@ export type alertContextType = {
removeFromNotificationList: (index: string) => void;
loading: boolean;
setLoading: (newState: boolean) => void;
+ isTweakPage: boolean;
+ setIsTweakPage: (newState: boolean) => void;
};
export type darkContextType = {
diff --git a/src/frontend/src/types/utils/reactflowUtils.ts b/src/frontend/src/types/utils/reactflowUtils.ts
index ecbbda4e4..9944fdba4 100644
--- a/src/frontend/src/types/utils/reactflowUtils.ts
+++ b/src/frontend/src/types/utils/reactflowUtils.ts
@@ -1,4 +1,4 @@
-import { Edge } from "reactflow";
+import { Edge, Node } from "reactflow";
import { NodeType } from "../flow";
export type cleanEdgesType = {
@@ -8,3 +8,8 @@ export type cleanEdgesType = {
};
updateEdge: (edge: Edge[]) => void;
};
+
+export type unselectAllNodesType = {
+ updateNodes: (nodes: Node[]) => void;
+ data: Node[] | null;
+};
diff --git a/src/frontend/src/utils/reactflowUtils.ts b/src/frontend/src/utils/reactflowUtils.ts
index 13c3973f1..87971ef9b 100644
--- a/src/frontend/src/utils/reactflowUtils.ts
+++ b/src/frontend/src/utils/reactflowUtils.ts
@@ -2,13 +2,17 @@ import _ from "lodash";
import {
Connection,
Edge,
+ Node,
ReactFlowInstance,
ReactFlowJsonObject,
} from "reactflow";
import { specialCharsRegex } from "../constants/constants";
import { APITemplateType } from "../types/api";
import { FlowType, NodeType } from "../types/flow";
-import { cleanEdgesType } from "../types/utils/reactflowUtils";
+import {
+ cleanEdgesType,
+ unselectAllNodesType,
+} from "../types/utils/reactflowUtils";
import { toNormalCase } from "./utils";
export function cleanEdges({
@@ -55,6 +59,14 @@ export function cleanEdges({
updateEdge(newEdges);
}
+export function unselectAllNodes({ updateNodes, data }: unselectAllNodesType) {
+ let newNodes = _.cloneDeep(data);
+ newNodes!.forEach((node: Node) => {
+ node.selected = false;
+ });
+ updateNodes(newNodes!);
+}
+
export function isValidConnection(
{ source, target, sourceHandle, targetHandle }: Connection,
reactFlowInstance: ReactFlowInstance
@@ -247,7 +259,6 @@ export function handleKeyDown(
inputValue: string | string[] | null,
block: string
) {
- console.log(e, inputValue, block);
//condition to fix bug control+backspace on Windows/Linux
if (
(typeof inputValue === "string" &&
diff --git a/src/frontend/src/utils/styleUtils.ts b/src/frontend/src/utils/styleUtils.ts
index ffeb4c650..31a7adf7e 100644
--- a/src/frontend/src/utils/styleUtils.ts
+++ b/src/frontend/src/utils/styleUtils.ts
@@ -19,6 +19,8 @@ import {
Edit,
Eraser,
ExternalLink,
+ Eye,
+ EyeOff,
File,
FileDown,
FileSearch,
@@ -33,6 +35,7 @@ import {
HelpCircle,
Home,
Info,
+ Key,
Laptop2,
Layers,
Lightbulb,
@@ -59,7 +62,9 @@ import {
TerminalSquare,
Trash2,
Undo,
+ Unplug,
Upload,
+ UserCog2,
UserMinus2,
UserPlus2,
Users2,
@@ -290,4 +295,9 @@ export const nodeIconsLucide: iconsType = {
ChevronsLeft,
FaGithub,
FaApple,
+ EyeOff,
+ Eye,
+ UserCog2,
+ Key,
+ Unplug,
};
diff --git a/src/frontend/tailwind.config.js b/src/frontend/tailwind.config.js
index 52330ae92..5130f3fcf 100644
--- a/src/frontend/tailwind.config.js
+++ b/src/frontend/tailwind.config.js
@@ -201,6 +201,12 @@ module.exports = {
".dark .theme-attribution .react-flow__attribution a": {
color: "black",
},
+ ".text-align-last-left": {
+ "text-align-last": "left",
+ },
+ ".text-align-last-right": {
+ "text-align-last": "right",
+ },
});
}),
require("@tailwindcss/typography"),
diff --git a/tests/test_cache.py b/tests/test_cache.py
index 50698c304..edf205a05 100644
--- a/tests/test_cache.py
+++ b/tests/test_cache.py
@@ -1,4 +1,6 @@
import json
+from langflow.services.database.models.base import orjson_dumps
+import orjson
from langflow.graph import Graph
import pytest
@@ -63,9 +65,9 @@ def test_cache_size_limit(basic_data_graph):
nodes = modified_data_graph["nodes"]
node_id = nodes[0]["id"]
# Now we replace all instances ode node_id with a new id in the json
- json_string = json.dumps(modified_data_graph)
+ json_string = orjson_dumps(modified_data_graph)
modified_json_string = json_string.replace(node_id, f"{node_id}_{i}")
- modified_data_graph_new_id = json.loads(modified_json_string)
+ modified_data_graph_new_id = orjson.loads(modified_json_string)
build_langchain_object_with_caching(modified_data_graph_new_id)
assert len(build_langchain_object_with_caching.cache) == 10
diff --git a/tests/test_database.py b/tests/test_database.py
index 996d4faa5..48e253026 100644
--- a/tests/test_database.py
+++ b/tests/test_database.py
@@ -1,4 +1,5 @@
-import json
+from langflow.services.database.models.base import orjson_dumps
+import orjson
import pytest
from uuid import UUID, uuid4
@@ -16,7 +17,7 @@ def json_style():
# color: str = Field(index=True)
# emoji: str = Field(index=False)
# flow_id: UUID = Field(default=None, foreign_key="flow.id")
- return json.dumps(
+ return orjson_dumps(
{
"color": "red",
"emoji": "👍",
@@ -25,7 +26,7 @@ def json_style():
def test_create_flow(client: TestClient, json_flow: str):
- flow = json.loads(json_flow)
+ flow = orjson.loads(json_flow)
data = flow["data"]
flow = FlowCreate(name="Test Flow", description="description", data=data)
response = client.post("api/v1/flows/", json=flow.dict())
@@ -41,7 +42,7 @@ def test_create_flow(client: TestClient, json_flow: str):
def test_read_flows(client: TestClient, json_flow: str):
- flow_data = json.loads(json_flow)
+ flow_data = orjson.loads(json_flow)
data = flow_data["data"]
flow = FlowCreate(name="Test Flow", description="description", data=data)
response = client.post("api/v1/flows/", json=flow.dict())
@@ -61,7 +62,7 @@ def test_read_flows(client: TestClient, json_flow: str):
def test_read_flow(client: TestClient, json_flow: str):
- flow = json.loads(json_flow)
+ flow = orjson.loads(json_flow)
data = flow["data"]
flow = FlowCreate(name="Test Flow", description="description", data=data)
response = client.post("api/v1/flows/", json=flow.dict())
@@ -76,7 +77,7 @@ def test_read_flow(client: TestClient, json_flow: str):
def test_update_flow(client: TestClient, json_flow: str):
- flow = json.loads(json_flow)
+ flow = orjson.loads(json_flow)
data = flow["data"]
flow = FlowCreate(name="Test Flow", description="description", data=data)
@@ -97,7 +98,7 @@ def test_update_flow(client: TestClient, json_flow: str):
def test_delete_flow(client: TestClient, json_flow: str):
- flow = json.loads(json_flow)
+ flow = orjson.loads(json_flow)
data = flow["data"]
flow = FlowCreate(name="Test Flow", description="description", data=data)
response = client.post("api/v1/flows/", json=flow.dict())
@@ -108,7 +109,7 @@ def test_delete_flow(client: TestClient, json_flow: str):
def test_create_flows(client: TestClient, session: Session, json_flow: str):
- flow = json.loads(json_flow)
+ flow = orjson.loads(json_flow)
data = flow["data"]
# Create test data
flow_list = FlowListCreate(
@@ -133,7 +134,7 @@ def test_create_flows(client: TestClient, session: Session, json_flow: str):
def test_upload_file(client: TestClient, session: Session, json_flow: str):
- flow = json.loads(json_flow)
+ flow = orjson.loads(json_flow)
data = flow["data"]
# Create test data
flow_list = FlowListCreate(
@@ -142,7 +143,7 @@ def test_upload_file(client: TestClient, session: Session, json_flow: str):
FlowCreate(name="Flow 2", description="description", data=data),
]
)
- file_contents = json.dumps(flow_list.dict())
+ file_contents = orjson_dumps(flow_list.dict())
response = client.post(
"api/v1/flows/upload/",
files={"file": ("examples.json", file_contents, "application/json")},
@@ -161,7 +162,7 @@ def test_upload_file(client: TestClient, session: Session, json_flow: str):
def test_download_file(client: TestClient, session: Session, json_flow):
- flow = json.loads(json_flow)
+ flow = orjson.loads(json_flow)
data = flow["data"]
# Create test data
flow_list = FlowListCreate(
@@ -202,7 +203,7 @@ def test_get_nonexistent_flow(client: TestClient):
def test_update_flow_idempotency(client: TestClient, json_flow: str):
- flow_data = json.loads(json_flow)
+ flow_data = orjson.loads(json_flow)
data = flow_data["data"]
flow_data = FlowCreate(name="Test Flow", description="description", data=data)
response = client.post("api/v1/flows/", json=flow_data.dict())
@@ -214,7 +215,7 @@ def test_update_flow_idempotency(client: TestClient, json_flow: str):
def test_update_nonexistent_flow(client: TestClient, json_flow: str):
- flow_data = json.loads(json_flow)
+ flow_data = orjson.loads(json_flow)
data = flow_data["data"]
uuid = uuid4()
updated_flow = FlowCreate(
diff --git a/tests/test_loading.py b/tests/test_loading.py
index 11fa8e471..e5c409c93 100644
--- a/tests/test_loading.py
+++ b/tests/test_loading.py
@@ -1,5 +1,4 @@
import json
-
import pytest
from langchain.chains.base import Chain
from langflow.processing.process import load_flow_from_json