From 6990d4865d9fa60b50fbf9deabdd9a746b960cd9 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Thu, 31 Aug 2023 14:35:12 -0300 Subject: [PATCH 01/33] Admin page UI revamp --- .../components/PaginatorComponent/index.tsx | 37 +- .../src/components/loadingComponent/index.tsx | 4 +- src/frontend/src/constants/constants.ts | 15 + src/frontend/src/controllers/API/index.ts | 9 +- src/frontend/src/modals/ApiModal/index.tsx | 4 +- .../src/modals/UserManagementModal/index.tsx | 3 +- src/frontend/src/modals/baseModal/index.tsx | 29 +- .../src/modals/codeAreaModal/index.tsx | 3 +- src/frontend/src/modals/exportModal/index.tsx | 13 +- src/frontend/src/pages/AdminPage/index.tsx | 489 +++++++++--------- src/frontend/src/style/applies.css | 8 + src/frontend/src/types/components/index.ts | 3 +- src/frontend/src/utils/reactflowUtils.ts | 4 +- src/frontend/src/utils/styleUtils.ts | 2 + 14 files changed, 318 insertions(+), 305 deletions(-) diff --git a/src/frontend/src/components/PaginatorComponent/index.tsx b/src/frontend/src/components/PaginatorComponent/index.tsx index 5b285e031..fa759a8bb 100644 --- a/src/frontend/src/components/PaginatorComponent/index.tsx +++ b/src/frontend/src/components/PaginatorComponent/index.tsx @@ -12,17 +12,15 @@ import { Button } from "../ui/button"; export default function PaginatorComponent({ pageSize = 10, - pageIndex = 0, + pageIndex = 1, rowsCount = [10, 20, 50, 100], totalRowsCount = 0, paginate, }: PaginatorComponentType) { const [size, setPageSize] = useState(pageSize); - const [index, setPageIndex] = useState(pageIndex); const [maxIndex, setMaxPageIndex] = useState( Math.ceil(totalRowsCount / pageSize) ); - const [currentPage, setCurrentPage] = useState(1); useEffect(() => { setMaxPageIndex(Math.ceil(totalRowsCount / size)); @@ -39,8 +37,9 @@ export default function PaginatorComponent({ onValueChange={(pageSize: string) => { setPageSize(Number(pageSize)); setMaxPageIndex(Math.ceil(totalRowsCount / Number(pageSize))); - paginate(Number(pageSize), 0); + paginate(Number(pageSize), 1); }} + value={pageSize.toString()} > @@ -55,30 +54,25 @@ export default function PaginatorComponent({
- Page {currentPage} of {maxIndex} + Page {pageIndex} of {maxIndex}
- )} -
-
- { - handleNewUser(user); - }} - > - - -
- - {loadingUsers && ( -
- Loading... -
- )} -
- - - - Id - Username - Active - Superuser - Created At - Updated At - - - - {!loadingUsers && ( - - {filterUserList.map( - (user: UserInputType, index) => ( - - - - - {user.id} - - - - - - - {user.username} - - - - - { - handleDisableUser( - user.is_active, - user.id, - user - ); - }} - > - - - - - { - handleSuperUserEdit( - user.is_superuser, - user.id, - user - ); - }} - > - - - - - { - new Date(user.create_at!) - .toISOString() - .split("T")[0] - } - - - { - new Date(user.updated_at!) - .toISOString() - .split("T")[0] - } - - -
- { - handleEditUser(user.id, editUser); - }} - > - - - - - - { - handleDeleteUser(user); - }} - > - - - - -
-
-
- ) - )} -
- )} -
-
- - { - handleChangePagination(pageSize, pageIndex); - }} - > - +
+ {userData && ( +
+
+ + + {ADMIN_HEADER_TITLE} + +
+ + {ADMIN_HEADER_DESCRIPTION} + +
+
+ handleFilterUsers(e.target.value)} + /> + {inputValue.length > 0 ? ( +
{ + setInputValue(""); + setFilterUserList(userList.current); + }} + > +
-
+ ) : ( +
+ +
+ )} +
+
+ { + handleNewUser(user); + }} + asChild + > + +
- )} - + {loadingUsers ? ( +
+ +
+ ) : userList.current.length === 0 ? ( + <> +
+ No users registered. +
+ + ) : ( + <> +
+ + + + Id + Username + Active + Superuser + Created At + Updated At + + + + {!loadingUsers && ( + + {filterUserList.map((user: UserInputType, index) => ( + + + + {user.id} + + + + + + {user.username} + + + + + { + handleDisableUser( + user.is_active, + user.id, + user + ); + }} + > + + + + + { + handleSuperUserEdit( + user.is_superuser, + user.id, + user + ); + }} + > + + + + + { + new Date(user.create_at!) + .toISOString() + .split("T")[0] + } + + + { + new Date(user.updated_at!) + .toISOString() + .split("T")[0] + } + + +
+ { + handleEditUser(user.id, editUser); + }} + > + + + + + + { + handleDeleteUser(user); + }} + > + + + + +
+
+
+ ))} +
+ )} +
+
+ + { + handleChangePagination(pageIndex, pageSize); + }} + > + + )} + + )} ); } diff --git a/src/frontend/src/style/applies.css b/src/frontend/src/style/applies.css index 685d81b05..9396cdc38 100644 --- a/src/frontend/src/style/applies.css +++ b/src/frontend/src/style/applies.css @@ -212,6 +212,10 @@ @apply flex-max-width h-full flex-col overflow-auto bg-muted px-16; } + .admin-page-panel { + @apply flex-max-width h-full flex-col overflow-auto bg-muted px-16; + } + .main-page-nav-arrangement { @apply flex-max-width justify-between px-6 py-12 pb-2; } @@ -228,6 +232,10 @@ @apply flex w-[60%] px-6 pb-14 text-muted-foreground; } + .admin-page-description-text { + @apply flex w-[80%] px-6 pb-8 text-muted-foreground; + } + .main-page-flows-display { @apply grid w-full gap-4 p-4 md:grid-cols-2 lg:grid-cols-4; } diff --git a/src/frontend/src/types/components/index.ts b/src/frontend/src/types/components/index.ts index b61ddf07d..511586de0 100644 --- a/src/frontend/src/types/components/index.ts +++ b/src/frontend/src/types/components/index.ts @@ -253,6 +253,7 @@ export type UserManagementType = { icon: string; data?: any; index?: number; + asChild?: boolean; onConfirm: (index, data) => void; }; @@ -548,5 +549,5 @@ export type fetchErrorComponentType = { export type dropdownButtonPropsType = { firstButtonName: string; onFirstBtnClick: () => void; - options: Array<{ name: string; onBtnClick: () => void; }>; + options: Array<{ name: string; onBtnClick: () => void }>; }; diff --git a/src/frontend/src/utils/reactflowUtils.ts b/src/frontend/src/utils/reactflowUtils.ts index 87971ef9b..9d06de64c 100644 --- a/src/frontend/src/utils/reactflowUtils.ts +++ b/src/frontend/src/utils/reactflowUtils.ts @@ -255,7 +255,9 @@ export function addVersionToDuplicates(flow: FlowType, flows: FlowType[]) { } export function handleKeyDown( - e: React.KeyboardEvent, + e: + | React.KeyboardEvent + | React.KeyboardEvent, inputValue: string | string[] | null, block: string ) { diff --git a/src/frontend/src/utils/styleUtils.ts b/src/frontend/src/utils/styleUtils.ts index e74bb0e34..bea021dfc 100644 --- a/src/frontend/src/utils/styleUtils.ts +++ b/src/frontend/src/utils/styleUtils.ts @@ -58,6 +58,7 @@ import { Scissors, Search, Settings2, + Shield, Sparkles, SunIcon, TerminalSquare, @@ -260,6 +261,7 @@ export const nodeIconsLucide: iconsType = { Bell, ChevronLeft, ChevronDown, + Shield, Plus, Redo, Settings2, From 2265d947bbf11265911434f172945541af5ea5c0 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Thu, 31 Aug 2023 14:35:54 -0300 Subject: [PATCH 02/33] Formatting fixes --- .../DropdownButtonComponent/index.tsx | 4 +--- src/frontend/src/contexts/tabsContext.tsx | 6 ++++-- src/frontend/src/pages/MainPage/index.tsx | 19 +++++++++++++------ 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/frontend/src/components/DropdownButtonComponent/index.tsx b/src/frontend/src/components/DropdownButtonComponent/index.tsx index 1ddddb3c1..784a08528 100644 --- a/src/frontend/src/components/DropdownButtonComponent/index.tsx +++ b/src/frontend/src/components/DropdownButtonComponent/index.tsx @@ -39,9 +39,7 @@ export default function DropdownButton({ }} > {!showOptions ? ( - + ) : ( )} diff --git a/src/frontend/src/contexts/tabsContext.tsx b/src/frontend/src/contexts/tabsContext.tsx index 339ad67b5..49de70974 100644 --- a/src/frontend/src/contexts/tabsContext.tsx +++ b/src/frontend/src/contexts/tabsContext.tsx @@ -315,9 +315,11 @@ export function TabsProvider({ children }: { children: ReactNode }) { input.type = "file"; input.accept = ".json"; // add a change event listener to the file input - id = await new Promise(resolve => { + id = await new Promise((resolve) => { input.onchange = async (e: Event) => { - if ((e.target as HTMLInputElement).files![0].type === "application/json") { + if ( + (e.target as HTMLInputElement).files![0].type === "application/json" + ) { const currentfile = (e.target as HTMLInputElement).files![0]; let text = await currentfile.text(); let flow: FlowType = JSON.parse(text); diff --git a/src/frontend/src/pages/MainPage/index.tsx b/src/frontend/src/pages/MainPage/index.tsx index 364519ea8..9582d4c57 100644 --- a/src/frontend/src/pages/MainPage/index.tsx +++ b/src/frontend/src/pages/MainPage/index.tsx @@ -1,5 +1,6 @@ import { useContext, useEffect } from "react"; import { Link, useNavigate } from "react-router-dom"; +import DropdownButton from "../../components/DropdownButtonComponent"; import { CardComponent } from "../../components/cardComponent"; import IconComponent from "../../components/genericIconComponent"; import Header from "../../components/headerComponent"; @@ -7,7 +8,6 @@ import { SkeletonCardComponent } from "../../components/skeletonCardComponent"; import { Button } from "../../components/ui/button"; import { USER_PROJECTS_HEADER } from "../../constants/constants"; import { TabsContext } from "../../contexts/tabsContext"; -import DropdownButton from "../../components/DropdownButtonComponent"; export default function HomePage(): JSX.Element { const { flows, @@ -15,12 +15,19 @@ export default function HomePage(): JSX.Element { downloadFlows, uploadFlows, addFlow, - removeFlow, uploadFlow, + removeFlow, + uploadFlow, isLoading, } = useContext(TabsContext); - const dropdownOptions = [{name: "Import from JSON", onBtnClick: () => uploadFlow(true).then((id) => { - navigate("/flow/" + id); - })}] + const dropdownOptions = [ + { + name: "Import from JSON", + onBtnClick: () => + uploadFlow(true).then((id) => { + navigate("/flow/" + id); + }), + }, + ]; // Set a null id useEffect(() => { @@ -73,7 +80,7 @@ export default function HomePage(): JSX.Element { - Manage your personal projects. Download or upload your collection. + Manage your personal projects. Download or upload your collection.
{isLoading && flows.length == 0 ? ( From 6d634313e3ce438b6e0b7b1c62ae95e76313f4f3 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Thu, 31 Aug 2023 14:36:47 -0300 Subject: [PATCH 03/33] Change input placeholder --- src/frontend/src/pages/AdminPage/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/src/pages/AdminPage/index.tsx b/src/frontend/src/pages/AdminPage/index.tsx index fedf6f37b..86ae63f36 100644 --- a/src/frontend/src/pages/AdminPage/index.tsx +++ b/src/frontend/src/pages/AdminPage/index.tsx @@ -204,7 +204,7 @@ export default function AdminPage() {
handleFilterUsers(e.target.value)} /> From 28580095dd1855d519bbf9f9e92dfeb3a11208d0 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Thu, 31 Aug 2023 15:08:09 -0300 Subject: [PATCH 04/33] Changed types and resetted tab id when on Admin Page --- src/frontend/src/constants/constants.ts | 2 +- src/frontend/src/contexts/tabsContext.tsx | 1 - src/frontend/src/controllers/API/index.ts | 4 ++-- src/frontend/src/pages/AdminPage/LoginPage/index.tsx | 2 +- src/frontend/src/pages/AdminPage/index.tsx | 8 ++++++++ src/frontend/src/pages/MainPage/index.tsx | 4 ---- 6 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/frontend/src/constants/constants.ts b/src/frontend/src/constants/constants.ts index b30e4e9b7..e18de3327 100644 --- a/src/frontend/src/constants/constants.ts +++ b/src/frontend/src/constants/constants.ts @@ -504,7 +504,7 @@ export const USER_PROJECTS_HEADER = "My Collection"; * @constant * */ -export const ADMIN_HEADER_TITLE = "Welcome back!"; +export const ADMIN_HEADER_TITLE = "Admin Page"; /** * Header description for admin page diff --git a/src/frontend/src/contexts/tabsContext.tsx b/src/frontend/src/contexts/tabsContext.tsx index 49de70974..6190a6e56 100644 --- a/src/frontend/src/contexts/tabsContext.tsx +++ b/src/frontend/src/contexts/tabsContext.tsx @@ -238,7 +238,6 @@ export function TabsProvider({ children }: { children: ReactNode }) { function hardReset() { newNodeId.current = uid(); setTabId(""); - setFlows([]); setIsLoading(true); setId(uid()); diff --git a/src/frontend/src/controllers/API/index.ts b/src/frontend/src/controllers/API/index.ts index 12e60bb72..764658b7b 100644 --- a/src/frontend/src/controllers/API/index.ts +++ b/src/frontend/src/controllers/API/index.ts @@ -400,7 +400,7 @@ export async function renewAccessToken(token: string) { } } -export async function getLoggedUser(): Promise> { +export async function getLoggedUser(): Promise { try { const res = await api.get(`${BASE_URL_API}user`); @@ -411,7 +411,7 @@ export async function getLoggedUser(): Promise> { console.log("Error:", error); throw error; } - return []; + return null; } export async function addUser(user: UserInputType): Promise> { diff --git a/src/frontend/src/pages/AdminPage/LoginPage/index.tsx b/src/frontend/src/pages/AdminPage/LoginPage/index.tsx index 3a56b8e99..e0ceaad49 100644 --- a/src/frontend/src/pages/AdminPage/LoginPage/index.tsx +++ b/src/frontend/src/pages/AdminPage/LoginPage/index.tsx @@ -48,7 +48,7 @@ export default function LoginAdminPage() { } function getUser() { - if (getAuthentication) { + if (getAuthentication()) { setTimeout(() => { getLoggedUser() .then((user) => { diff --git a/src/frontend/src/pages/AdminPage/index.tsx b/src/frontend/src/pages/AdminPage/index.tsx index 86ae63f36..2a4dd9bac 100644 --- a/src/frontend/src/pages/AdminPage/index.tsx +++ b/src/frontend/src/pages/AdminPage/index.tsx @@ -22,6 +22,7 @@ import { } from "../../constants/constants"; import { alertContext } from "../../contexts/alertContext"; import { AuthContext } from "../../contexts/authContext"; +import { TabsContext } from "../../contexts/tabsContext"; import { addUser, deleteUser, @@ -43,6 +44,13 @@ export default function AdminPage() { const { userData } = useContext(AuthContext); const [totalRowsCount, setTotalRowsCount] = useState(0); + const { setTabId } = useContext(TabsContext); + + // set null id + useEffect(() => { + setTabId(""); + }, []); + const userList = useRef([]); useEffect(() => { diff --git a/src/frontend/src/pages/MainPage/index.tsx b/src/frontend/src/pages/MainPage/index.tsx index 9582d4c57..8365eb3f5 100644 --- a/src/frontend/src/pages/MainPage/index.tsx +++ b/src/frontend/src/pages/MainPage/index.tsx @@ -35,10 +35,6 @@ export default function HomePage(): JSX.Element { }, []); const navigate = useNavigate(); - useEffect(() => { - console.log(isLoading); - }, [isLoading]); - // Personal flows display return ( <> From da7bdb651379d51fbbecde82477ddba97d661d4f Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Thu, 31 Aug 2023 15:30:40 -0300 Subject: [PATCH 05/33] Added profile settings page --- .../src/components/headerComponent/index.tsx | 5 +++ .../src/pages/ProfileSettingsPage/index.tsx | 37 +++++++++++++++++++ src/frontend/src/routes.tsx | 9 +++++ src/frontend/src/utils/styleUtils.ts | 2 + 4 files changed, 53 insertions(+) create mode 100644 src/frontend/src/pages/ProfileSettingsPage/index.tsx diff --git a/src/frontend/src/components/headerComponent/index.tsx b/src/frontend/src/components/headerComponent/index.tsx index 2a7219fed..5d8c61c4a 100644 --- a/src/frontend/src/components/headerComponent/index.tsx +++ b/src/frontend/src/components/headerComponent/index.tsx @@ -153,6 +153,11 @@ export default function Header(): JSX.Element { Admin Page )} + navigate("/account/settings")} + > + Profile Settings + { logout(); diff --git a/src/frontend/src/pages/ProfileSettingsPage/index.tsx b/src/frontend/src/pages/ProfileSettingsPage/index.tsx new file mode 100644 index 000000000..2dc90515c --- /dev/null +++ b/src/frontend/src/pages/ProfileSettingsPage/index.tsx @@ -0,0 +1,37 @@ +import { useContext, useEffect } from "react"; +import IconComponent from "../../components/genericIconComponent"; +import Header from "../../components/headerComponent"; +import { Input } from "../../components/ui/input"; +import { alertContext } from "../../contexts/alertContext"; +import { TabsContext } from "../../contexts/tabsContext"; +export default function ProfileSettingsPage(): JSX.Element { + const { setTabId } = useContext(TabsContext); + + // set null id + useEffect(() => { + setTabId(""); + }, []); + const { setErrorData } = useContext(alertContext); + + return ( + <> +
+ +
+
+ + + Profile Settings + +
+ + Change your profile settings like your password and your profile + picture. + +
+ +
+
+ + ); +} diff --git a/src/frontend/src/routes.tsx b/src/frontend/src/routes.tsx index cd8f86c9a..e9e6f1858 100644 --- a/src/frontend/src/routes.tsx +++ b/src/frontend/src/routes.tsx @@ -9,6 +9,7 @@ import ApiKeysPage from "./pages/ApiKeysPage"; import CommunityPage from "./pages/CommunityPage"; import FlowPage from "./pages/FlowPage"; import HomePage from "./pages/MainPage"; +import ProfileSettingsPage from "./pages/ProfileSettingsPage"; import ViewPage from "./pages/ViewPage"; import DeleteAccountPage from "./pages/deleteAccountPage"; import LoginPage from "./pages/loginPage"; @@ -95,6 +96,14 @@ const Router = () => { /> + + + + } + /> Date: Tue, 5 Sep 2023 11:52:20 -0300 Subject: [PATCH 06/33] Added style to file drop --- .../src/pages/CommunityPage/index.tsx | 13 ++- src/frontend/src/pages/MainPage/index.tsx | 105 ++++++++++++------ src/frontend/src/utils/styleUtils.ts | 2 + 3 files changed, 86 insertions(+), 34 deletions(-) diff --git a/src/frontend/src/pages/CommunityPage/index.tsx b/src/frontend/src/pages/CommunityPage/index.tsx index c96bbdaf4..7f808f035 100644 --- a/src/frontend/src/pages/CommunityPage/index.tsx +++ b/src/frontend/src/pages/CommunityPage/index.tsx @@ -7,6 +7,7 @@ import { useNavigate } from "react-router-dom"; import { CardComponent } from "../../components/cardComponent"; import IconComponent from "../../components/genericIconComponent"; import Header from "../../components/headerComponent"; +import { SkeletonCardComponent } from "../../components/skeletonCardComponent"; import { getExamples } from "../../controllers/API"; import { FlowType } from "../../types/flow"; export default function CommunityPage(): JSX.Element { @@ -74,7 +75,14 @@ export default function CommunityPage(): JSX.Element { new and powerful features.
- {!loadingExamples && + {loadingExamples ? ( + <> + + + + + + ) : ( examples.map((flow, idx) => ( } /> - ))} + )) + )}
diff --git a/src/frontend/src/pages/MainPage/index.tsx b/src/frontend/src/pages/MainPage/index.tsx index 8365eb3f5..067a63fcc 100644 --- a/src/frontend/src/pages/MainPage/index.tsx +++ b/src/frontend/src/pages/MainPage/index.tsx @@ -1,4 +1,4 @@ -import { useContext, useEffect } from "react"; +import { useContext, useEffect, useState } from "react"; import { Link, useNavigate } from "react-router-dom"; import DropdownButton from "../../components/DropdownButtonComponent"; import { CardComponent } from "../../components/cardComponent"; @@ -35,6 +35,28 @@ export default function HomePage(): JSX.Element { }, []); const navigate = useNavigate(); + const [isDragging, setIsDragging] = useState(false); + + const dragOver = (e) => { + e.preventDefault(); + setIsDragging(true); + }; + + const dragEnter = (e) => { + e.preventDefault(); + setIsDragging(true); + }; + + const dragLeave = () => { + setIsDragging(false); + }; + + const fileDrop = (e) => { + e.preventDefault(); + setIsDragging(false); + // Handle file drop logic here + }; + // Personal flows display return ( <> @@ -78,40 +100,59 @@ export default function HomePage(): JSX.Element { Manage your personal projects. Download or upload your collection. -
- {isLoading && flows.length == 0 ? ( +
+ {isDragging ? ( <> - - - - + + Drop your flow here ) : ( - flows.map((flow, idx) => ( - - - - } - onDelete={() => { - removeFlow(flow.id); - }} - /> - )) +
+ {isLoading && flows.length == 0 ? ( + <> + + + + + + ) : ( + flows.map((flow, idx) => ( + + + + } + onDelete={() => { + removeFlow(flow.id); + }} + /> + )) + )} +
)}
diff --git a/src/frontend/src/utils/styleUtils.ts b/src/frontend/src/utils/styleUtils.ts index caba6fc77..ec3692c80 100644 --- a/src/frontend/src/utils/styleUtils.ts +++ b/src/frontend/src/utils/styleUtils.ts @@ -1,4 +1,5 @@ import { + ArrowUpToLine, Bell, Check, CheckCircle2, @@ -188,6 +189,7 @@ export const nodeNames: { [char: string]: string } = { }; export const nodeIconsLucide: iconsType = { + ArrowUpToLine: ArrowUpToLine, Chroma: ChromaIcon, AirbyteJSONLoader: AirbyteIcon, Anthropic: AnthropicIcon, From 2c99d2ce70fad266207b32decd29c0ec5a06a26b Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Tue, 5 Sep 2023 14:14:47 -0300 Subject: [PATCH 07/33] Implemented flow add when dropping json --- src/frontend/src/pages/MainPage/index.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/frontend/src/pages/MainPage/index.tsx b/src/frontend/src/pages/MainPage/index.tsx index 067a63fcc..a599844eb 100644 --- a/src/frontend/src/pages/MainPage/index.tsx +++ b/src/frontend/src/pages/MainPage/index.tsx @@ -54,7 +54,9 @@ export default function HomePage(): JSX.Element { const fileDrop = (e) => { e.preventDefault(); setIsDragging(false); - // Handle file drop logic here + if (e.dataTransfer.types.some((types) => types === "Files")) { + uploadFlow(true, e.dataTransfer.files.item(0)!); + } }; // Personal flows display From 109b9a117276235d156b97ffe1de0d11734bdcb3 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Tue, 5 Sep 2023 14:21:47 -0300 Subject: [PATCH 08/33] Added error checking when file is not a JSON --- .../components/PageComponent/index.tsx | 9 +++++++- src/frontend/src/pages/MainPage/index.tsx | 21 +++++++++++++++---- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx b/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx index 620170fbb..4d8a4b3f4 100644 --- a/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx +++ b/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx @@ -298,7 +298,14 @@ export default function Page({ setNodes((nds) => nds.concat(newNode)); } else if (event.dataTransfer.types.some((types) => types === "Files")) { takeSnapshot(); - uploadFlow(false, event.dataTransfer.files.item(0)!); + if (event.dataTransfer.files.item(0)!.type === "application/json") { + uploadFlow(true, event.dataTransfer.files.item(0)!); + } else { + setErrorData({ + title: "Invalid file type", + list: ["Please upload a JSON file"], + }); + } } }, // Specify dependencies for useCallback diff --git a/src/frontend/src/pages/MainPage/index.tsx b/src/frontend/src/pages/MainPage/index.tsx index a599844eb..f45e4cfc8 100644 --- a/src/frontend/src/pages/MainPage/index.tsx +++ b/src/frontend/src/pages/MainPage/index.tsx @@ -7,6 +7,7 @@ import Header from "../../components/headerComponent"; import { SkeletonCardComponent } from "../../components/skeletonCardComponent"; import { Button } from "../../components/ui/button"; import { USER_PROJECTS_HEADER } from "../../constants/constants"; +import { alertContext } from "../../contexts/alertContext"; import { TabsContext } from "../../contexts/tabsContext"; export default function HomePage(): JSX.Element { const { @@ -19,6 +20,7 @@ export default function HomePage(): JSX.Element { uploadFlow, isLoading, } = useContext(TabsContext); + const { setErrorData } = useContext(alertContext); const dropdownOptions = [ { name: "Import from JSON", @@ -55,7 +57,14 @@ export default function HomePage(): JSX.Element { e.preventDefault(); setIsDragging(false); if (e.dataTransfer.types.some((types) => types === "Files")) { - uploadFlow(true, e.dataTransfer.files.item(0)!); + if (e.dataTransfer.files.item(0).type === "application/json") { + uploadFlow(true, e.dataTransfer.files.item(0)!); + } else { + setErrorData({ + title: "Invalid file type", + list: ["Please upload a JSON file"], + }); + } } }; @@ -108,14 +117,18 @@ export default function HomePage(): JSX.Element { onDragLeave={dragLeave} onDrop={fileDrop} className={ - "h-full w-full " + (isDragging - ? "flex flex-col items-center justify-center mb-24 gap-4 text-2xl font-light" + "h-full w-full " + + (isDragging + ? "mb-24 flex flex-col items-center justify-center gap-4 text-2xl font-light" : "") } > {isDragging ? ( <> - + Drop your flow here ) : ( From b6c5fecae24e19ce57567f474776d5db7d60e784 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Tue, 5 Sep 2023 15:20:51 -0300 Subject: [PATCH 09/33] Fixed message not sending when closing db --- src/frontend/src/modals/formModal/index.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/frontend/src/modals/formModal/index.tsx b/src/frontend/src/modals/formModal/index.tsx index 2982b8738..b76df56f2 100644 --- a/src/frontend/src/modals/formModal/index.tsx +++ b/src/frontend/src/modals/formModal/index.tsx @@ -194,7 +194,8 @@ export default function FormModal({ } function handleWsMessage(data: any) { - if (Array.isArray(data)) { + console.log(data); + if (Array.isArray(data) && data.length > 0) { //set chat history setChatHistory((_) => { let newChatHistory: ChatMessageType[] = []; @@ -313,7 +314,7 @@ export default function FormModal({ } }; // do not add connectWS on dependencies array - }, []); + }, [open]); useEffect(() => { if ( From a7db3862773c90e56db931af6e7fff82abb7a3b6 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 5 Sep 2023 17:19:11 -0300 Subject: [PATCH 10/33] =?UTF-8?q?=F0=9F=90=9B=20fix(users.py):=20change=20?= =?UTF-8?q?route=20paths=20for=20user-related=20endpoints=20to=20improve?= =?UTF-8?q?=20consistency=20and=20readability=20=E2=9C=A8=20feat(users.py)?= =?UTF-8?q?:=20add=20support=20for=20resetting=20a=20user's=20password=20?= =?UTF-8?q?=F0=9F=90=9B=20fix(crud.py):=20fix=20update=5Fuser=20function?= =?UTF-8?q?=20to=20properly=20handle=20unchanged=20attributes=20and=20retu?= =?UTF-8?q?rn=20304=20status=20code=20if=20nothing=20is=20updated=20?= =?UTF-8?q?=F0=9F=90=9B=20fix(user.py):=20change=20field=20name=20in=20Use?= =?UTF-8?q?rUpdate=20model=20from=20'username'=20to=20'password'=20to=20re?= =?UTF-8?q?flect=20the=20intended=20functionality=20=F0=9F=90=9B=20fix(ind?= =?UTF-8?q?ex.ts):=20update=20API=20routes=20in=20frontend=20controller=20?= =?UTF-8?q?functions=20to=20match=20the=20changed=20user-related=20endpoin?= =?UTF-8?q?t=20paths?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/api/v1/users.py | 52 +++++++++++++++---- .../services/database/models/user/crud.py | 18 ++++--- .../services/database/models/user/user.py | 2 +- src/frontend/src/controllers/API/index.ts | 10 ++-- 4 files changed, 60 insertions(+), 22 deletions(-) diff --git a/src/backend/langflow/api/v1/users.py b/src/backend/langflow/api/v1/users.py index 517dd7f69..deb350414 100644 --- a/src/backend/langflow/api/v1/users.py +++ b/src/backend/langflow/api/v1/users.py @@ -20,13 +20,14 @@ from langflow.services.auth.utils import ( get_password_hash, ) from langflow.services.database.models.user.crud import ( + get_user_by_id, update_user, ) -router = APIRouter(tags=["Users"]) +router = APIRouter(tags=["Users"], prefix="/users") -@router.post("/user", response_model=UserRead, status_code=201) +@router.post("/", response_model=UserRead, status_code=201) def add_user( user: UserCreate, session: Session = Depends(get_session), @@ -50,7 +51,7 @@ def add_user( return new_user -@router.get("/user", response_model=UserRead) +@router.get("/whoami", response_model=UserRead) def read_current_user( current_user: User = Depends(get_current_active_user), ) -> User: @@ -60,7 +61,7 @@ def read_current_user( return current_user -@router.get("/users", response_model=UsersResponse) +@router.get("/", response_model=UsersResponse) def read_all_users( skip: int = 0, limit: int = 10, @@ -82,20 +83,53 @@ def read_all_users( ) -@router.patch("/user/{user_id}", response_model=UserRead) +@router.patch("/{user_id}", response_model=UserRead) def patch_user( user_id: UUID, - user: UserUpdate, - _: Session = Depends(get_current_active_user), + user_update: UserUpdate, + user: Session = Depends(get_current_active_user), session: Session = Depends(get_session), ) -> User: """ Update an existing user's data. """ - return update_user(user_id, user, session) + if not user.is_superuser and user.id != user_id: + raise HTTPException( + status_code=403, detail="You don't have the permission to update this user" + ) + + if user_db := get_user_by_id(session, user_id): + return update_user(user_db, user_update, session) + else: + raise HTTPException(status_code=404, detail="User not found") -@router.delete("/user/{user_id}") +@router.patch("/{user_id}/reset-password", response_model=UserRead) +def reset_password( + user_id: UUID, + user_update: UserUpdate, + user: Session = Depends(get_current_active_user), + session: Session = Depends(get_session), +) -> User: + """ + Reset a user's password. + """ + if user_id != user.id: + raise HTTPException( + status_code=400, detail="You can't change another user's password" + ) + + if not user: + raise HTTPException(status_code=404, detail="User not found") + + user.password = get_password_hash(user_update.password) + session.commit() + session.refresh(user) + + return user + + +@router.delete("/{user_id}", response_model=dict) def delete_user( user_id: UUID, current_user: User = Depends(get_current_active_superuser), diff --git a/src/backend/langflow/services/database/models/user/crud.py b/src/backend/langflow/services/database/models/user/crud.py index 3dc02a499..93d5dd801 100644 --- a/src/backend/langflow/services/database/models/user/crud.py +++ b/src/backend/langflow/services/database/models/user/crud.py @@ -20,20 +20,24 @@ def get_user_by_id(db: Session, id: UUID) -> Union[User, None]: def update_user( - user_id: UUID, user: UserUpdate, db: Session = Depends(get_session) + user_db: User, user: UserUpdate, db: Session = Depends(get_session) ) -> User: - user_db = get_user_by_id(db, user_id) if not user_db: raise HTTPException(status_code=404, detail="User not found") - user_db_by_username = get_user_by_username(db, user.username) # type: ignore - if user_db_by_username and user_db_by_username.id != user_id: - raise HTTPException(status_code=409, detail="Username already exists") + # user_db_by_username = get_user_by_username(db, user.username) # type: ignore + # if user_db_by_username and user_db_by_username.id != user_id: + # raise HTTPException(status_code=409, detail="Username already exists") user_data = user.dict(exclude_unset=True) + changed = False for attr, value in user_data.items(): if hasattr(user_db, attr) and value is not None: setattr(user_db, attr, value) + changed = True + + if not changed: + raise HTTPException(status_code=304, detail="Nothing to update") user_db.updated_at = datetime.now(timezone.utc) flag_modified(user_db, "updated_at") @@ -49,5 +53,5 @@ def update_user( def update_user_last_login_at(user_id: UUID, db: Session = Depends(get_session)): user_data = UserUpdate(last_login_at=datetime.now(timezone.utc)) # type: ignore - - return update_user(user_id, user_data, db) + user = get_user_by_id(db, user_id) + return update_user(user, user_data, db) diff --git a/src/backend/langflow/services/database/models/user/user.py b/src/backend/langflow/services/database/models/user/user.py index 5f83b4d88..b58f7226a 100644 --- a/src/backend/langflow/services/database/models/user/user.py +++ b/src/backend/langflow/services/database/models/user/user.py @@ -40,7 +40,7 @@ class UserRead(SQLModel): class UserUpdate(SQLModel): - username: Optional[str] = Field() + password: Optional[str] = Field() is_active: Optional[bool] = Field() is_superuser: Optional[bool] = Field() last_login_at: Optional[datetime] = Field() diff --git a/src/frontend/src/controllers/API/index.ts b/src/frontend/src/controllers/API/index.ts index 2d18b7cfe..182f2c802 100644 --- a/src/frontend/src/controllers/API/index.ts +++ b/src/frontend/src/controllers/API/index.ts @@ -402,7 +402,7 @@ export async function renewAccessToken(token: string) { export async function getLoggedUser(): Promise { try { - const res = await api.get(`${BASE_URL_API}user`); + const res = await api.get(`${BASE_URL_API}users/whoami`); if (res.status === 200) { return res.data; @@ -416,7 +416,7 @@ export async function getLoggedUser(): Promise { export async function addUser(user: UserInputType): Promise> { try { - const res = await api.post(`${BASE_URL_API}user`, user); + const res = await api.post(`${BASE_URL_API}users/`, user); if (res.status === 200) { return res.data; } @@ -433,7 +433,7 @@ export async function getUsersPage( ): Promise> { try { const res = await api.get( - `${BASE_URL_API}users?skip=${skip}&limit=${limit}` + `${BASE_URL_API}users/?skip=${skip}&limit=${limit}` ); if (res.status === 200) { return res.data; @@ -447,7 +447,7 @@ export async function getUsersPage( export async function deleteUser(user_id: string) { try { - const res = await api.delete(`${BASE_URL_API}user/${user_id}`); + const res = await api.delete(`${BASE_URL_API}users/${user_id}`); if (res.status === 200) { return res.data; } @@ -459,7 +459,7 @@ export async function deleteUser(user_id: string) { export async function updateUser(user_id: string, user: Users) { try { - const res = await api.patch(`${BASE_URL_API}user/${user_id}`, user); + const res = await api.patch(`${BASE_URL_API}users/${user_id}`, user); if (res.status === 200) { return res.data; } From 49167a58a9005234d00bec29e0fee0de62f5b205 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 5 Sep 2023 17:19:46 -0300 Subject: [PATCH 11/33] =?UTF-8?q?=F0=9F=90=9B=20fix(tests):=20update=20end?= =?UTF-8?q?point=20path=20from=20"/api/v1/user"=20to=20"/api/v1/users"=20f?= =?UTF-8?q?or=20consistency=20and=20clarity=20=E2=9C=A8=20feat(tests):=20a?= =?UTF-8?q?dd=20assertion=20for=20response=20status=20code=20201=20after?= =?UTF-8?q?=20creating=20a=20user=20=F0=9F=90=9B=20fix(tests):=20update=20?= =?UTF-8?q?endpoint=20path=20from=20"/api/v1/user/{user=5Fid}"=20to=20"/ap?= =?UTF-8?q?i/v1/users/{user=5Fid}"=20for=20consistency=20and=20clarity=20?= =?UTF-8?q?=F0=9F=90=9B=20fix(tests):=20update=20endpoint=20path=20from=20?= =?UTF-8?q?"/api/v1/user"=20to=20"/api/v1/users/whoami"=20to=20fetch=20the?= =?UTF-8?q?=20updated=20user=20=F0=9F=90=9B=20fix(tests):=20update=20endpo?= =?UTF-8?q?int=20path=20from=20"/api/v1/user/{user=5Fid}"=20to=20"/api/v1/?= =?UTF-8?q?users/{user=5Fid}"=20for=20consistency=20and=20clarity=20?= =?UTF-8?q?=F0=9F=90=9B=20fix(tests):=20update=20endpoint=20path=20from=20?= =?UTF-8?q?"/api/v1/user/{user=5Fid}"=20to=20"/api/v1/users/{user=5Fid}"?= =?UTF-8?q?=20for=20consistency=20and=20clarity=20=E2=9C=A8=20feat(tests):?= =?UTF-8?q?=20add=20assertion=20for=20response=20status=20code=20401=20and?= =?UTF-8?q?=20detail=20message=20after=20fetching=20the=20updated=20user?= =?UTF-8?q?=20=E2=9C=A8=20feat(tests):=20add=20assertion=20for=20response?= =?UTF-8?q?=20status=20code=20200=20after=20deleting=20a=20user=20?= =?UTF-8?q?=F0=9F=90=9B=20fix(tests):=20update=20endpoint=20path=20from=20?= =?UTF-8?q?"/api/v1/user/{user=5Fid}"=20to=20"/api/v1/users/{user=5Fid}"?= =?UTF-8?q?=20for=20consistency=20and=20clarity=20=E2=9C=A8=20feat(tests):?= =?UTF-8?q?=20add=20assertion=20for=20response=20status=20code=20200=20and?= =?UTF-8?q?=20detail=20message=20after=20deleting=20a=20user=20?= =?UTF-8?q?=F0=9F=90=9B=20fix(tests):=20update=20endpoint=20path=20from=20?= =?UTF-8?q?"/api/v1/user/{user=5Fid}"=20to=20"/api/v1/users/{user=5Fid}"?= =?UTF-8?q?=20for=20consistency=20and=20clarity=20=F0=9F=90=9B=20fix(tests?= =?UTF-8?q?):=20update=20endpoint=20path=20from=20"/api/v1/user/{user=5Fid?= =?UTF-8?q?}"=20to=20"/api/v1/users/{user=5Fid}"=20for=20consistency=20and?= =?UTF-8?q?=20clarity=20=E2=9C=A8=20feat(tests):=20add=20assertion=20for?= =?UTF-8?q?=20response=20status=20code=20304=20after=20patching=20user=20d?= =?UTF-8?q?ata=20=E2=9C=A8=20feat(tests):=20add=20assertion=20for=20respon?= =?UTF-8?q?se=20status=20code=20200=20after=20resetting=20user=20password?= =?UTF-8?q?=20=F0=9F=90=9B=20fix(tests):=20update=20endpoint=20path=20from?= =?UTF-8?q?=20"/api/v1/user/{user=5Fid}"=20to=20"/api/v1/users/{user=5Fid}?= =?UTF-8?q?"=20for=20consistency=20and=20clarity=20=E2=9C=A8=20feat(tests)?= =?UTF-8?q?:=20add=20assertion=20for=20response=20status=20code=20422=20an?= =?UTF-8?q?d=20detail=20message=20after=20patching=20user=20data=20with=20?= =?UTF-8?q?wrong=20id=20=F0=9F=90=9B=20fix(tests):=20update=20endpoint=20p?= =?UTF-8?q?ath=20from=20"/api/v1/user/{user=5Fid}"=20to=20"/api/v1/users/{?= =?UTF-8?q?user=5Fid}"=20for=20consistency=20and=20clarity=20=E2=9C=A8=20f?= =?UTF-8?q?eat(tests):=20add=20assertion=20for=20response?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/conftest.py | 3 ++- tests/test_user.py | 52 +++++++++++++++++++++++++++------------------- 2 files changed, 33 insertions(+), 22 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 1d1fb9ac7..95aba4462 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -166,7 +166,8 @@ def test_user(client): username="testuser", password="testpassword", ) - response = client.post("/api/v1/user", json=user_data.dict()) + response = client.post("/api/v1/users", json=user_data.dict()) + assert response.status_code == 201 return response.json() diff --git a/tests/test_user.py b/tests/test_user.py index bc617e127..4d99c5c9c 100644 --- a/tests/test_user.py +++ b/tests/test_user.py @@ -78,24 +78,27 @@ def test_deactivated_user_cannot_access(client, deactivated_user, logged_in_head assert response.json()["detail"] == "The user doesn't have enough privileges" -def test_data_consistency_after_update(client, active_user, logged_in_headers): +def test_data_consistency_after_update( + client, active_user, logged_in_headers, super_user_headers +): user_id = active_user.id - update_data = UserUpdate(username="newname") + update_data = UserUpdate(is_active=False) response = client.patch( - f"/api/v1/user/{user_id}", json=update_data.dict(), headers=logged_in_headers + f"/api/v1/users/{user_id}", json=update_data.dict(), headers=super_user_headers ) - assert response.status_code == 200 + assert response.status_code == 200, response.json() # Fetch the updated user from the database - response = client.get("/api/v1/user", headers=logged_in_headers) - assert response.json()["username"] == "newname", response.json() + response = client.get("/api/v1/users/whoami", headers=logged_in_headers) + assert response.status_code == 401, response.json() + assert response.json()["detail"] == "Could not validate credentials" def test_data_consistency_after_delete(client, test_user, super_user_headers): - user_id = test_user["id"] - response = client.delete(f"/api/v1/user/{user_id}", headers=super_user_headers) - assert response.status_code == 200 + user_id = test_user.get("id") + response = client.delete(f"/api/v1/users/{user_id}", headers=super_user_headers) + assert response.status_code == 200, response.json() # Attempt to fetch the deleted user from the database response = client.get("/api/v1/users", headers=super_user_headers) @@ -157,7 +160,21 @@ def test_patch_user(client, active_user, logged_in_headers): ) response = client.patch( - f"/api/v1/user/{user_id}", json=update_data.dict(), headers=logged_in_headers + f"/api/v1/users/{user_id}", json=update_data.dict(), headers=logged_in_headers + ) + assert response.status_code == 304, response.json() + + +def test_patch_reset_password(client, active_user, logged_in_headers): + user_id = active_user.id + update_data = UserUpdate( + password="newpassword", + ) + + response = client.patch( + f"/api/v1/users/{user_id}/reset-password", + json=update_data.dict(), + headers=logged_in_headers, ) assert response.status_code == 200, response.json() @@ -169,7 +186,7 @@ def test_patch_user_wrong_id(client, active_user, logged_in_headers): ) response = client.patch( - f"/api/v1/user/{user_id}", json=update_data.dict(), headers=logged_in_headers + f"/api/v1/users/{user_id}", json=update_data.dict(), headers=logged_in_headers ) assert response.status_code == 422, response.json() assert response.json() == { @@ -185,14 +202,14 @@ def test_patch_user_wrong_id(client, active_user, logged_in_headers): def test_delete_user(client, test_user, super_user_headers): user_id = test_user["id"] - response = client.delete(f"/api/v1/user/{user_id}", headers=super_user_headers) + response = client.delete(f"/api/v1/users/{user_id}", headers=super_user_headers) assert response.status_code == 200 assert response.json() == {"detail": "User deleted"} def test_delete_user_wrong_id(client, test_user, super_user_headers): user_id = "wrong_id" - response = client.delete(f"/api/v1/user/{user_id}", headers=super_user_headers) + response = client.delete(f"/api/v1/users/{user_id}", headers=super_user_headers) assert response.status_code == 422 assert response.json() == { "detail": [ @@ -207,13 +224,6 @@ def test_delete_user_wrong_id(client, test_user, super_user_headers): def test_normal_user_cant_delete_user(client, test_user, logged_in_headers): user_id = test_user["id"] - response = client.delete(f"/api/v1/user/{user_id}", headers=logged_in_headers) + response = client.delete(f"/api/v1/users/{user_id}", headers=logged_in_headers) assert response.status_code == 400 assert response.json() == {"detail": "The user doesn't have enough privileges"} - - -# If you still want to test the superuser endpoint -def test_add_super_user_for_testing_purposes_delete_me_before_merge_into_dev(client): - response = client.post("/api/v1/super_user") - assert response.status_code == 200 - assert response.json()["username"] == "superuser" From 512db102fadc015317ba174f8181f1e3524377ab Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Wed, 6 Sep 2023 14:16:07 -0300 Subject: [PATCH 12/33] Change password implemented, but not working --- src/frontend/src/constants/constants.ts | 5 + src/frontend/src/controllers/API/index.ts | 13 +++ .../src/pages/ProfileSettingsPage/index.tsx | 106 +++++++++++++++++- src/frontend/src/pages/loginPage/index.tsx | 2 +- src/frontend/src/types/components/index.ts | 5 + 5 files changed, 125 insertions(+), 6 deletions(-) diff --git a/src/frontend/src/constants/constants.ts b/src/frontend/src/constants/constants.ts index e18de3327..b5b2c266e 100644 --- a/src/frontend/src/constants/constants.ts +++ b/src/frontend/src/constants/constants.ts @@ -534,6 +534,11 @@ export const CONTROL_INPUT_STATE = { username: "", }; +export const CONTROL_RESET_PASSWORD_STATE = { + password: "", + cnfPassword: "", +}; + export const CONTROL_LOGIN_STATE = { username: "", password: "", diff --git a/src/frontend/src/controllers/API/index.ts b/src/frontend/src/controllers/API/index.ts index 182f2c802..406ba1862 100644 --- a/src/frontend/src/controllers/API/index.ts +++ b/src/frontend/src/controllers/API/index.ts @@ -427,6 +427,19 @@ export async function addUser(user: UserInputType): Promise> { return []; } +export async function resetPassword(user_id: string, password: string): Promise> { + try { + const res = await api.post(`${BASE_URL_API}users/${user_id}/reset-password`, {password}); + if (res.status === 200) { + return res.data; + } + } catch (error) { + console.log("Error:", error); + throw error; + } + return []; +} + export async function getUsersPage( skip: number, limit: number diff --git a/src/frontend/src/pages/ProfileSettingsPage/index.tsx b/src/frontend/src/pages/ProfileSettingsPage/index.tsx index 2dc90515c..642b3aa73 100644 --- a/src/frontend/src/pages/ProfileSettingsPage/index.tsx +++ b/src/frontend/src/pages/ProfileSettingsPage/index.tsx @@ -1,17 +1,49 @@ -import { useContext, useEffect } from "react"; +import * as Form from "@radix-ui/react-form"; +import { useContext, useEffect, useState } from "react"; import IconComponent from "../../components/genericIconComponent"; import Header from "../../components/headerComponent"; -import { Input } from "../../components/ui/input"; +import InputComponent from "../../components/inputComponent"; +import { Button } from "../../components/ui/button"; +import { CONTROL_RESET_PASSWORD_STATE } from "../../constants/constants"; import { alertContext } from "../../contexts/alertContext"; +import { AuthContext } from "../../contexts/authContext"; import { TabsContext } from "../../contexts/tabsContext"; +import { resetPassword } from "../../controllers/API"; +import { + inputHandlerEventType, + resetPasswordInputStateType, +} from "../../types/components"; export default function ProfileSettingsPage(): JSX.Element { const { setTabId } = useContext(TabsContext); + const [inputState, setInputState] = useState( + CONTROL_RESET_PASSWORD_STATE + ); + // set null id useEffect(() => { setTabId(""); }, []); const { setErrorData } = useContext(alertContext); + const { userData } = useContext(AuthContext); + const { password, cnfPassword } = inputState; + + function changePassword() { + if (password !== cnfPassword) { + setErrorData({ + title: "Error changing password", + list: ["Passwords do not match"], + }); + return; + } + resetPassword(userData!.id, password); + } + + function handleInput({ + target: { name, value }, + }: inputHandlerEventType): void { + setInputState((prev) => ({ ...prev, [name]: value })); + } return ( <> @@ -28,9 +60,73 @@ export default function ProfileSettingsPage(): JSX.Element { Change your profile settings like your password and your profile picture. -
- -
+ { + changePassword(); + const data = Object.fromEntries(new FormData(event.currentTarget)); + event.preventDefault(); + }} + className="flex justify-between px-6" + > +
+
+ + + Password * + + + + { + handleInput({ target: { name: "password", value } }); + }} + value={password} + isForm + password={true} + required + placeholder="Password" + className="w-full" + /> + + + + Please enter your password + + +
+
+ + + Confirm Password{" "} + * + + + { + handleInput({ target: { name: "cnfPassword", value } }); + }} + value={cnfPassword} + isForm + password={true} + required + placeholder="Confirm Password" + className="w-full" + /> + + + Please confirm your password + + +
+
+
+ + + +
+
); diff --git a/src/frontend/src/pages/loginPage/index.tsx b/src/frontend/src/pages/loginPage/index.tsx index 061d4ac0b..ff9d53e82 100644 --- a/src/frontend/src/pages/loginPage/index.tsx +++ b/src/frontend/src/pages/loginPage/index.tsx @@ -54,7 +54,7 @@ export default function LoginPage(): JSX.Element { setTimeout(() => { getLoggedUser() .then((user) => { - const isSuperUser = user.is_superuser; + const isSuperUser = user!.is_superuser; setIsAdmin(isSuperUser); setUserData(user); }) diff --git a/src/frontend/src/types/components/index.ts b/src/frontend/src/types/components/index.ts index 511586de0..a5413f3a9 100644 --- a/src/frontend/src/types/components/index.ts +++ b/src/frontend/src/types/components/index.ts @@ -262,6 +262,11 @@ export type loginInputStateType = { password: string; }; +export type resetPasswordInputStateType = { + password: string; + cnfPassword: string; +}; + export type UserInputType = { username: string; password: string; From 04544b172f5482ffba36e05cf7a0e84125998eef Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 8 Sep 2023 07:36:56 -0300 Subject: [PATCH 13/33] =?UTF-8?q?=F0=9F=9A=80=20feat(alembic):=20add=20mig?= =?UTF-8?q?ration=20to=20add=20profile=5Fimage=20column=20to=20user=20tabl?= =?UTF-8?q?e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🔧 chore(user.py): add profile_image field to User model for storing user profile images --- .../67cc006d50bf_add_profile_image_column.py | 39 +++++++++++++++++++ .../services/database/models/user/user.py | 2 + 2 files changed, 41 insertions(+) create mode 100644 src/backend/langflow/alembic/versions/67cc006d50bf_add_profile_image_column.py diff --git a/src/backend/langflow/alembic/versions/67cc006d50bf_add_profile_image_column.py b/src/backend/langflow/alembic/versions/67cc006d50bf_add_profile_image_column.py new file mode 100644 index 000000000..5a3cbc448 --- /dev/null +++ b/src/backend/langflow/alembic/versions/67cc006d50bf_add_profile_image_column.py @@ -0,0 +1,39 @@ +"""Add profile-image column + +Revision ID: 67cc006d50bf +Revises: 260dbcc8b680 +Create Date: 2023-09-08 07:36:13.387318 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +import sqlmodel + + +# revision identifiers, used by Alembic. +revision: str = "67cc006d50bf" +down_revision: Union[str, None] = "260dbcc8b680" +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table("user", schema=None) as batch_op: + batch_op.add_column( + sa.Column( + "profile_image", sqlmodel.sql.sqltypes.AutoString(), nullable=True + ) + ) + + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table("user", schema=None) as batch_op: + batch_op.drop_column("profile_image") + + # ### end Alembic commands ### diff --git a/src/backend/langflow/services/database/models/user/user.py b/src/backend/langflow/services/database/models/user/user.py index b58f7226a..105f6281b 100644 --- a/src/backend/langflow/services/database/models/user/user.py +++ b/src/backend/langflow/services/database/models/user/user.py @@ -15,6 +15,7 @@ class User(SQLModelSerializable, table=True): id: UUID = Field(default_factory=uuid4, primary_key=True, unique=True) username: str = Field(index=True, unique=True) password: str = Field() + profile_image: Optional[str] = Field(default=None) is_active: bool = Field(default=False) is_superuser: bool = Field(default=False) create_at: datetime = Field(default_factory=datetime.utcnow) @@ -40,6 +41,7 @@ class UserRead(SQLModel): class UserUpdate(SQLModel): + profile_image: Optional[str] = Field() password: Optional[str] = Field() is_active: Optional[bool] = Field() is_superuser: Optional[bool] = Field() From b770622603b3165b216727b9097c6e35c7e560f4 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Fri, 8 Sep 2023 11:23:57 -0300 Subject: [PATCH 14/33] Added gradient and changed types of patch user --- .../src/components/headerComponent/index.tsx | 2 +- src/frontend/src/constants/constants.ts | 3 +- src/frontend/src/controllers/API/index.ts | 16 +------ .../src/pages/ProfileSettingsPage/index.tsx | 47 ++++++++++++++----- src/frontend/src/types/api/index.ts | 8 ++++ src/frontend/src/types/components/index.ts | 3 +- 6 files changed, 50 insertions(+), 29 deletions(-) diff --git a/src/frontend/src/components/headerComponent/index.tsx b/src/frontend/src/components/headerComponent/index.tsx index e6702e342..69b493e89 100644 --- a/src/frontend/src/components/headerComponent/index.tsx +++ b/src/frontend/src/components/headerComponent/index.tsx @@ -140,7 +140,7 @@ export default function Header(): JSX.Element { className={ "h-7 w-7 rounded-full focus-visible:outline-0 " + gradients[ - parseInt(userData?.id ?? "", 10) % gradients.length + parseInt(userData?.id ?? "", 30) % gradients.length ] } /> diff --git a/src/frontend/src/constants/constants.ts b/src/frontend/src/constants/constants.ts index b5b2c266e..a7c8b111b 100644 --- a/src/frontend/src/constants/constants.ts +++ b/src/frontend/src/constants/constants.ts @@ -534,9 +534,10 @@ export const CONTROL_INPUT_STATE = { username: "", }; -export const CONTROL_RESET_PASSWORD_STATE = { +export const CONTROL_PATCH_USER_STATE = { password: "", cnfPassword: "", + gradient: "", }; export const CONTROL_LOGIN_STATE = { diff --git a/src/frontend/src/controllers/API/index.ts b/src/frontend/src/controllers/API/index.ts index 406ba1862..15e315f97 100644 --- a/src/frontend/src/controllers/API/index.ts +++ b/src/frontend/src/controllers/API/index.ts @@ -6,6 +6,7 @@ import { APIObjectType, LoginType, Users, + changeUser, sendAllProps, } from "../../types/api/index"; import { UserInputType } from "../../types/components"; @@ -427,19 +428,6 @@ export async function addUser(user: UserInputType): Promise> { return []; } -export async function resetPassword(user_id: string, password: string): Promise> { - try { - const res = await api.post(`${BASE_URL_API}users/${user_id}/reset-password`, {password}); - if (res.status === 200) { - return res.data; - } - } catch (error) { - console.log("Error:", error); - throw error; - } - return []; -} - export async function getUsersPage( skip: number, limit: number @@ -470,7 +458,7 @@ export async function deleteUser(user_id: string) { } } -export async function updateUser(user_id: string, user: Users) { +export async function updateUser(user_id: string, user: changeUser) { try { const res = await api.patch(`${BASE_URL_API}users/${user_id}`, user); if (res.status === 200) { diff --git a/src/frontend/src/pages/ProfileSettingsPage/index.tsx b/src/frontend/src/pages/ProfileSettingsPage/index.tsx index 642b3aa73..189b7c23f 100644 --- a/src/frontend/src/pages/ProfileSettingsPage/index.tsx +++ b/src/frontend/src/pages/ProfileSettingsPage/index.tsx @@ -4,20 +4,20 @@ import IconComponent from "../../components/genericIconComponent"; import Header from "../../components/headerComponent"; import InputComponent from "../../components/inputComponent"; import { Button } from "../../components/ui/button"; -import { CONTROL_RESET_PASSWORD_STATE } from "../../constants/constants"; +import { CONTROL_PATCH_USER_STATE } from "../../constants/constants"; import { alertContext } from "../../contexts/alertContext"; import { AuthContext } from "../../contexts/authContext"; import { TabsContext } from "../../contexts/tabsContext"; -import { resetPassword } from "../../controllers/API"; +import { updateUser } from "../../controllers/API"; import { inputHandlerEventType, - resetPasswordInputStateType, + patchUserInputStateType, } from "../../types/components"; export default function ProfileSettingsPage(): JSX.Element { const { setTabId } = useContext(TabsContext); - const [inputState, setInputState] = useState( - CONTROL_RESET_PASSWORD_STATE + const [inputState, setInputState] = useState( + CONTROL_PATCH_USER_STATE ); // set null id @@ -26,9 +26,9 @@ export default function ProfileSettingsPage(): JSX.Element { }, []); const { setErrorData } = useContext(alertContext); const { userData } = useContext(AuthContext); - const { password, cnfPassword } = inputState; + const { password, cnfPassword, gradient } = inputState; - function changePassword() { + function handlePatchUser() { if (password !== cnfPassword) { setErrorData({ title: "Error changing password", @@ -36,7 +36,8 @@ export default function ProfileSettingsPage(): JSX.Element { }); return; } - resetPassword(userData!.id, password); + + updateUser(userData!.id, {password, profile_image: gradient}); } function handleInput({ @@ -62,7 +63,7 @@ export default function ProfileSettingsPage(): JSX.Element { { - changePassword(); + handlePatchUser(); const data = Object.fromEntries(new FormData(event.currentTarget)); event.preventDefault(); }} @@ -70,7 +71,7 @@ export default function ProfileSettingsPage(): JSX.Element { >
- + Password * @@ -95,7 +96,7 @@ export default function ProfileSettingsPage(): JSX.Element {
- + Confirm Password{" "} * @@ -117,12 +118,34 @@ export default function ProfileSettingsPage(): JSX.Element { Please confirm your password + + + Insert Gradient{" "} + * + + + { + handleInput({ target: { name: "gradient", value } }); + }} + value={gradient} + isForm + password={true} + required + placeholder="Insert Gradient" + className="w-full" + /> + + + Please insert gradient + +
diff --git a/src/frontend/src/types/api/index.ts b/src/frontend/src/types/api/index.ts index b690d7134..247e3b2a2 100644 --- a/src/frontend/src/types/api/index.ts +++ b/src/frontend/src/types/api/index.ts @@ -78,6 +78,14 @@ export type LoginAuthType = { token_type?: string; }; +export type changeUser = { + username?: string; + is_active?: boolean; + is_superuser?: boolean; + password?: string; + profile_image?: string; +} + export type Users = { id: string; username: string; diff --git a/src/frontend/src/types/components/index.ts b/src/frontend/src/types/components/index.ts index a5413f3a9..51db60dbd 100644 --- a/src/frontend/src/types/components/index.ts +++ b/src/frontend/src/types/components/index.ts @@ -262,9 +262,10 @@ export type loginInputStateType = { password: string; }; -export type resetPasswordInputStateType = { +export type patchUserInputStateType = { password: string; cnfPassword: string; + gradient: string; }; export type UserInputType = { From bd4450050826cf2fb50e6c63ac5bf89b40f617f3 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Fri, 8 Sep 2023 13:08:39 -0300 Subject: [PATCH 15/33] Added gradient chooser and success messages --- .../gradientChooserComponent/index.tsx | 13 ++ .../src/components/headerComponent/index.tsx | 4 +- src/frontend/src/contexts/authContext.tsx | 2 +- src/frontend/src/controllers/API/index.ts | 2 + .../src/pages/ProfileSettingsPage/index.tsx | 136 +++++++++--------- src/frontend/src/types/api/index.ts | 1 + 6 files changed, 86 insertions(+), 72 deletions(-) create mode 100644 src/frontend/src/components/gradientChooserComponent/index.tsx diff --git a/src/frontend/src/components/gradientChooserComponent/index.tsx b/src/frontend/src/components/gradientChooserComponent/index.tsx new file mode 100644 index 000000000..1209e6064 --- /dev/null +++ b/src/frontend/src/components/gradientChooserComponent/index.tsx @@ -0,0 +1,13 @@ +import { useState } from "react"; +import { gradients } from "../../utils/styleUtils"; + + +export default function GradientChooserComponent({value, onChange}){ + return ( +
+ {gradients.map((gradient, idx) => +
{onChange(gradient)}} className={"w-12 h-12 rounded-full transition-all duration-400 " + gradient + (value === gradient ? " shadow-lg ring-2 ring-primary" : "")} key={idx}>
+ )} +
+ ) +} \ No newline at end of file diff --git a/src/frontend/src/components/headerComponent/index.tsx b/src/frontend/src/components/headerComponent/index.tsx index 69b493e89..7b0e32373 100644 --- a/src/frontend/src/components/headerComponent/index.tsx +++ b/src/frontend/src/components/headerComponent/index.tsx @@ -139,9 +139,7 @@ export default function Header(): JSX.Element { - + +
+
+ + + +
diff --git a/src/frontend/src/types/api/index.ts b/src/frontend/src/types/api/index.ts index 247e3b2a2..d693badf0 100644 --- a/src/frontend/src/types/api/index.ts +++ b/src/frontend/src/types/api/index.ts @@ -91,6 +91,7 @@ export type Users = { username: string; is_active: boolean; is_superuser: boolean; + profile_image: string; create_at: Date; updated_at: Date; }; From 071ad8621b9a9c21ae6d88df3ef18056404a809c Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 8 Sep 2023 14:13:54 -0300 Subject: [PATCH 16/33] =?UTF-8?q?=F0=9F=94=A7=20chore(user.py):=20add=20op?= =?UTF-8?q?tional=20profile=5Fimage=20field=20to=20UserRead=20model=20for?= =?UTF-8?q?=20improved=20user=20profile=20functionality?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/services/database/models/user/user.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/backend/langflow/services/database/models/user/user.py b/src/backend/langflow/services/database/models/user/user.py index 105f6281b..8bd42af00 100644 --- a/src/backend/langflow/services/database/models/user/user.py +++ b/src/backend/langflow/services/database/models/user/user.py @@ -33,6 +33,7 @@ class UserCreate(SQLModel): class UserRead(SQLModel): id: UUID = Field(default_factory=uuid4) username: str = Field() + profile_image: Optional[str] = Field() is_active: bool = Field() is_superuser: bool = Field() create_at: datetime = Field() From 75f03b17f48c79c7963fb4bc89a7745cea5e5feb Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 8 Sep 2023 14:19:28 -0300 Subject: [PATCH 17/33] =?UTF-8?q?=F0=9F=94=A7=20chore(67cc006d50bf=5Fadd?= =?UTF-8?q?=5Fprofile=5Fimage=5Fcolumn.py):=20refactor=20upgrade=20and=20d?= =?UTF-8?q?owngrade=20functions=20to=20check=20if=20"user"=20table=20exist?= =?UTF-8?q?s=20and=20if=20"profile=5Fimage"=20column=20is=20already=20pres?= =?UTF-8?q?ent=20before=20adding=20or=20dropping=20the=20column=20respecti?= =?UTF-8?q?vely?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../67cc006d50bf_add_profile_image_column.py | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/backend/langflow/alembic/versions/67cc006d50bf_add_profile_image_column.py b/src/backend/langflow/alembic/versions/67cc006d50bf_add_profile_image_column.py index 5a3cbc448..ca0aa0542 100644 --- a/src/backend/langflow/alembic/versions/67cc006d50bf_add_profile_image_column.py +++ b/src/backend/langflow/alembic/versions/67cc006d50bf_add_profile_image_column.py @@ -10,7 +10,7 @@ from typing import Sequence, Union from alembic import op import sqlalchemy as sa import sqlmodel - +from sqlalchemy.engine.reflection import Inspector # revision identifiers, used by Alembic. revision: str = "67cc006d50bf" @@ -21,19 +21,29 @@ depends_on: Union[str, Sequence[str], None] = None def upgrade() -> None: # ### commands auto generated by Alembic - please adjust! ### - with op.batch_alter_table("user", schema=None) as batch_op: - batch_op.add_column( - sa.Column( - "profile_image", sqlmodel.sql.sqltypes.AutoString(), nullable=True + conn = op.get_bind() + inspector = Inspector.from_engine(conn) + if "user" in inspector.get_table_names() and "profile_image" not in [ + column["name"] for column in inspector.get_columns("user") + ]: + with op.batch_alter_table("user", schema=None) as batch_op: + batch_op.add_column( + sa.Column( + "profile_image", sqlmodel.sql.sqltypes.AutoString(), nullable=True + ) ) - ) # ### end Alembic commands ### def downgrade() -> None: # ### commands auto generated by Alembic - please adjust! ### - with op.batch_alter_table("user", schema=None) as batch_op: - batch_op.drop_column("profile_image") + conn = op.get_bind() + inspector = Inspector.from_engine(conn) + if "user" in inspector.get_table_names() and "profile_image" in [ + column["name"] for column in inspector.get_columns("user") + ]: + with op.batch_alter_table("user", schema=None) as batch_op: + batch_op.drop_column("profile_image") # ### end Alembic commands ### From f95a38a4b502f0518b40de8ae50fa13095531af4 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 8 Sep 2023 14:23:19 -0300 Subject: [PATCH 18/33] =?UTF-8?q?=F0=9F=90=9B=20fix(crud.py):=20change=20H?= =?UTF-8?q?TTP=20status=20code=20from=20304=20to=20status.HTTP=5F304=5FNOT?= =?UTF-8?q?=5FMODIFIED=20for=20better=20readability=20and=20consistency=20?= =?UTF-8?q?=E2=9C=A8=20feat(crud.py):=20add=20support=20for=20updating=20u?= =?UTF-8?q?ser=20profile=20image=20in=20the=20update=5Fuser=20function=20?= =?UTF-8?q?=E2=9C=A8=20feat(test=5Fuser.py):=20add=20test=20case=20for=20u?= =?UTF-8?q?pdating=20user=20profile=20image=20in=20the=20test=5Fpatch=5Fus?= =?UTF-8?q?er=20function?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../langflow/services/database/models/user/crud.py | 7 ++++--- tests/test_user.py | 8 ++++++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/backend/langflow/services/database/models/user/crud.py b/src/backend/langflow/services/database/models/user/crud.py index 93d5dd801..abc79f49f 100644 --- a/src/backend/langflow/services/database/models/user/crud.py +++ b/src/backend/langflow/services/database/models/user/crud.py @@ -1,13 +1,12 @@ from datetime import datetime, timezone from typing import Union from uuid import UUID -from fastapi import Depends, HTTPException +from fastapi import Depends, HTTPException, status from langflow.services.database.models.user.user import User, UserUpdate from langflow.services.utils import get_session from sqlalchemy.exc import IntegrityError from sqlmodel import Session - from sqlalchemy.orm.attributes import flag_modified @@ -37,7 +36,9 @@ def update_user( changed = True if not changed: - raise HTTPException(status_code=304, detail="Nothing to update") + raise HTTPException( + status_code=status.HTTP_304_NOT_MODIFIED, detail="Nothing to update" + ) user_db.updated_at = datetime.now(timezone.utc) flag_modified(user_db, "updated_at") diff --git a/tests/test_user.py b/tests/test_user.py index 4d99c5c9c..4f2307624 100644 --- a/tests/test_user.py +++ b/tests/test_user.py @@ -163,6 +163,14 @@ def test_patch_user(client, active_user, logged_in_headers): f"/api/v1/users/{user_id}", json=update_data.dict(), headers=logged_in_headers ) assert response.status_code == 304, response.json() + update_data = UserUpdate( + profile_image="new_image", + ) + + response = client.patch( + f"/api/v1/users/{user_id}", json=update_data.dict(), headers=logged_in_headers + ) + assert response.status_code == 200, response.json() def test_patch_reset_password(client, active_user, logged_in_headers): From 01a289dd9a5cfe5bd5290152424c89a86dbbd57a Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 8 Sep 2023 14:28:32 -0300 Subject: [PATCH 19/33] =?UTF-8?q?=F0=9F=90=9B=20fix(users.py):=20assign=20?= =?UTF-8?q?the=20hashed=20new=20password=20to=20a=20variable=20before=20up?= =?UTF-8?q?dating=20the=20user's=20password=20to=20ensure=20consistency=20?= =?UTF-8?q?=E2=9C=85=20test(test=5Fuser.py):=20add=20test=20to=20verify=20?= =?UTF-8?q?if=20the=20new=20password=20works=20after=20resetting=20it?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/api/v1/users.py | 4 ++-- tests/test_user.py | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/backend/langflow/api/v1/users.py b/src/backend/langflow/api/v1/users.py index deb350414..81b0c2332 100644 --- a/src/backend/langflow/api/v1/users.py +++ b/src/backend/langflow/api/v1/users.py @@ -121,8 +121,8 @@ def reset_password( if not user: raise HTTPException(status_code=404, detail="User not found") - - user.password = get_password_hash(user_update.password) + new_password = get_password_hash(user_update.password) + user.password = new_password session.commit() session.refresh(user) diff --git a/tests/test_user.py b/tests/test_user.py index 4f2307624..54a713ef1 100644 --- a/tests/test_user.py +++ b/tests/test_user.py @@ -185,6 +185,10 @@ def test_patch_reset_password(client, active_user, logged_in_headers): headers=logged_in_headers, ) assert response.status_code == 200, response.json() + # Now we need to test if the new password works + login_data = {"username": active_user.username, "password": "newpassword"} + response = client.post("/api/v1/login", data=login_data) + assert response.status_code == 200 def test_patch_user_wrong_id(client, active_user, logged_in_headers): From a68535cbe4dc0aae7205f22beedb0f7e5fbf4ade Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 8 Sep 2023 16:42:56 -0300 Subject: [PATCH 20/33] =?UTF-8?q?=F0=9F=90=9B=20fix(users.py):=20prevent?= =?UTF-8?q?=20users=20from=20changing=20their=20password=20in=20the=20patc?= =?UTF-8?q?h=5Fuser=20endpoint=20to=20ensure=20security=20=F0=9F=90=9B=20f?= =?UTF-8?q?ix(users.py):=20prevent=20users=20from=20using=20their=20curren?= =?UTF-8?q?t=20password=20when=20resetting=20their=20password=20to=20ensur?= =?UTF-8?q?e=20security?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/api/v1/users.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/backend/langflow/api/v1/users.py b/src/backend/langflow/api/v1/users.py index 81b0c2332..dca8b23d2 100644 --- a/src/backend/langflow/api/v1/users.py +++ b/src/backend/langflow/api/v1/users.py @@ -97,6 +97,10 @@ def patch_user( raise HTTPException( status_code=403, detail="You don't have the permission to update this user" ) + if user.password: + raise HTTPException( + status_code=400, detail="You can't change your password here" + ) if user_db := get_user_by_id(session, user_id): return update_user(user_db, user_update, session) @@ -122,6 +126,10 @@ def reset_password( if not user: raise HTTPException(status_code=404, detail="User not found") new_password = get_password_hash(user_update.password) + if new_password == user.password: + raise HTTPException( + status_code=400, detail="You can't use your current password" + ) user.password = new_password session.commit() session.refresh(user) From ea11b16f580356b238f0a2a53a9152f111f99171 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 8 Sep 2023 16:44:25 -0300 Subject: [PATCH 21/33] =?UTF-8?q?=F0=9F=94=92=20chore(users.py):=20refacto?= =?UTF-8?q?r=20reset=5Fpassword=20function=20to=20improve=20password=20ver?= =?UTF-8?q?ification=20logic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/api/v1/users.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/backend/langflow/api/v1/users.py b/src/backend/langflow/api/v1/users.py index dca8b23d2..1c464ec4c 100644 --- a/src/backend/langflow/api/v1/users.py +++ b/src/backend/langflow/api/v1/users.py @@ -18,6 +18,7 @@ from langflow.services.auth.utils import ( get_current_active_superuser, get_current_active_user, get_password_hash, + verify_password, ) from langflow.services.database.models.user.crud import ( get_user_by_id, @@ -125,11 +126,11 @@ def reset_password( if not user: raise HTTPException(status_code=404, detail="User not found") - new_password = get_password_hash(user_update.password) - if new_password == user.password: + if verify_password(user_update.password, user.password): raise HTTPException( status_code=400, detail="You can't use your current password" ) + new_password = get_password_hash(user_update.password) user.password = new_password session.commit() session.refresh(user) From 705dd6acea5d20ea3439fe7a78477c69481ca896 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Fri, 8 Sep 2023 16:39:08 -0300 Subject: [PATCH 22/33] Fixed gradient not saving correctly --- .../gradientChooserComponent/index.tsx | 2 +- .../src/components/headerComponent/index.tsx | 2 +- .../src/pages/ProfileSettingsPage/index.tsx | 41 +++++++++++++------ 3 files changed, 31 insertions(+), 14 deletions(-) diff --git a/src/frontend/src/components/gradientChooserComponent/index.tsx b/src/frontend/src/components/gradientChooserComponent/index.tsx index 1209e6064..dccb93c68 100644 --- a/src/frontend/src/components/gradientChooserComponent/index.tsx +++ b/src/frontend/src/components/gradientChooserComponent/index.tsx @@ -6,7 +6,7 @@ export default function GradientChooserComponent({value, onChange}){ return (
{gradients.map((gradient, idx) => -
{onChange(gradient)}} className={"w-12 h-12 rounded-full transition-all duration-400 " + gradient + (value === gradient ? " shadow-lg ring-2 ring-primary" : "")} key={idx}>
+
{onChange(gradient)}} className={"cursor-pointer w-12 h-12 rounded-full transition-all duration-400 " + gradient + (value === gradient ? " shadow-lg ring-2 ring-primary" : "")} key={idx}>
)}
) diff --git a/src/frontend/src/components/headerComponent/index.tsx b/src/frontend/src/components/headerComponent/index.tsx index 7b0e32373..c73943e0c 100644 --- a/src/frontend/src/components/headerComponent/index.tsx +++ b/src/frontend/src/components/headerComponent/index.tsx @@ -139,7 +139,7 @@ export default function Header(): JSX.Element { )} diff --git a/src/frontend/src/pages/ProfileSettingsPage/index.tsx b/src/frontend/src/pages/ProfileSettingsPage/index.tsx index fc12d2fb3..091a7dc32 100644 --- a/src/frontend/src/pages/ProfileSettingsPage/index.tsx +++ b/src/frontend/src/pages/ProfileSettingsPage/index.tsx @@ -48,6 +48,8 @@ export default function ProfileSettingsPage(): JSX.Element { setUserData(newUserData); } + handleInput({ target: { name: "password", value: "" } }); + handleInput({ target: { name: "cnfPassword", value: "" } }); setSuccessData({ title: "Changes saved successfully!" }); } catch (error) { setErrorData({ title: "Error saving changes", list: [(error as any).response.data.detail] }); @@ -90,8 +92,6 @@ export default function ProfileSettingsPage(): JSX.Element { Password{" "} - - { handleInput({ target: { name: "password", value } }); @@ -102,8 +102,6 @@ export default function ProfileSettingsPage(): JSX.Element { placeholder="Password" className="w-full" /> - - Please enter your password diff --git a/src/frontend/src/types/components/index.ts b/src/frontend/src/types/components/index.ts index 51db60dbd..9b1db2ad2 100644 --- a/src/frontend/src/types/components/index.ts +++ b/src/frontend/src/types/components/index.ts @@ -233,6 +233,7 @@ export type PaginatorComponentType = { export type ConfirmationModalType = { title: string; titleHeader: string; + asChild?: boolean; modalContent: string; modalContentTitle: string; cancelText: string; From 77e9e7933267b265d06012d4dd12f756c4826d92 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Mon, 11 Sep 2023 16:06:27 -0300 Subject: [PATCH 28/33] Fix on api button --- .../pages/FlowPage/components/extraSidebarComponent/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/src/pages/FlowPage/components/extraSidebarComponent/index.tsx b/src/frontend/src/pages/FlowPage/components/extraSidebarComponent/index.tsx index c692fe59b..c677db635 100644 --- a/src/frontend/src/pages/FlowPage/components/extraSidebarComponent/index.tsx +++ b/src/frontend/src/pages/FlowPage/components/extraSidebarComponent/index.tsx @@ -100,7 +100,7 @@ export default function ExtraSidebar(): JSX.Element {
{flow && flow.data && ( -