diff --git a/backend/src/controllers/v2/secretsController.ts b/backend/src/controllers/v2/secretsController.ts index 13537df9c..23f9a6d0a 100644 --- a/backend/src/controllers/v2/secretsController.ts +++ b/backend/src/controllers/v2/secretsController.ts @@ -1,7 +1,7 @@ import { Types } from "mongoose"; import { Request, Response } from "express"; import { ISecret, Secret, ServiceTokenData } from "../../models"; -import { IAction, SecretVersion, EventType, AuditLog } from "../../ee/models"; +import { AuditLog, EventType, IAction, SecretVersion } from "../../ee/models"; import { ACTION_ADD_SECRETS, ACTION_DELETE_SECRETS, @@ -14,7 +14,7 @@ import { import { BadRequestError, UnauthorizedRequestError } from "../../utils/errors"; import { EventService } from "../../services"; import { eventPushSecrets } from "../../events"; -import { EELogService, EESecretService, EEAuditLogService } from "../../ee/services"; +import { EEAuditLogService, EELogService, EESecretService } from "../../ee/services"; import { SecretService, TelemetryService } from "../../services"; import { getUserAgentType } from "../../utils/posthog"; import { PERMISSION_WRITE_SECRETS } from "../../variables"; diff --git a/backend/src/ee/services/EEAuditLogService.ts b/backend/src/ee/services/EEAuditLogService.ts index 91fbc4252..eb5c1bbb3 100644 --- a/backend/src/ee/services/EEAuditLogService.ts +++ b/backend/src/ee/services/EEAuditLogService.ts @@ -16,7 +16,7 @@ type ValidEventScope = | Required export default class EEAuditLogService { - static async createAuditLog(authData: AuthData, event: Event, eventScope: ValidEventScope, shouldSave: boolean = true) { + static async createAuditLog(authData: AuthData, event: Event, eventScope: ValidEventScope, shouldSave = true) { const MS_IN_DAY = 24 * 60 * 60 * 1000; diff --git a/backend/src/routes/v1/userAction.ts b/backend/src/routes/v1/userAction.ts index 042f73c10..7fd26f783 100644 --- a/backend/src/routes/v1/userAction.ts +++ b/backend/src/routes/v1/userAction.ts @@ -6,7 +6,7 @@ import { userActionController } from "../../controllers/v1"; import { AuthMode } from "../../variables"; // note: [userAction] will be deprecated in /v2 in favor of [action] -router.post( +router.post( // TODO endpoint: move this into /users/me "/", requireAuth({ acceptedAuthModes: [AuthMode.JWT], diff --git a/frontend/src/components/basic/table/ProjectUsersTable.tsx b/frontend/src/components/basic/table/ProjectUsersTable.tsx index 022eda020..5a9795cff 100644 --- a/frontend/src/components/basic/table/ProjectUsersTable.tsx +++ b/frontend/src/components/basic/table/ProjectUsersTable.tsx @@ -4,12 +4,11 @@ import { faEye, faEyeSlash, faPenToSquare, faPlus, faX } from "@fortawesome/free import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider"; import { Select, SelectItem } from "@app/components/v2"; -import { useSubscription } from "@app/context"; +import { useSubscription, useWorkspace } from "@app/context"; import updateUserProjectPermission from "@app/ee/api/memberships/UpdateUserProjectPermission"; import changeUserRoleInWorkspace from "@app/pages/api/workspace/changeUserRoleInWorkspace"; import deleteUserFromWorkspace from "@app/pages/api/workspace/deleteUserFromWorkspace"; import getLatestFileKey from "@app/pages/api/workspace/getLatestFileKey"; -import getProjectInfo from "@app/pages/api/workspace/getProjectInfo"; import uploadKeys from "@app/pages/api/workspace/uploadKeys"; import { decryptAssymmetric, encryptAssymmetric } from "../../utilities/cryptography/crypto"; @@ -39,6 +38,7 @@ type EnvironmentProps = { * @returns */ const ProjectUsersTable = ({ userData, changeData, myUser, filter, isUserListLoading }: Props) => { + const { currentWorkspace } = useWorkspace(); const { subscription } = useSubscription(); const [roleSelected, setRoleSelected] = useState( Array(userData?.length).fill(userData.map((user) => user.role)) @@ -163,10 +163,11 @@ const ProjectUsersTable = ({ userData, changeData, myUser, filter, isUserListLoa useEffect(() => { setMyRole(userData.filter((user) => user.email === myUser)[0]?.role); (async () => { - const result = await getProjectInfo({ projectId: workspaceId }); - setWorkspaceEnvs(result.environments); + if (currentWorkspace) { + setWorkspaceEnvs(currentWorkspace.environments); + } })(); - }, [userData, myUser]); + }, [userData, myUser, currentWorkspace]); const grantAccess = async (id: string, publicKey: string) => { const result = await getLatestFileKey({ workspaceId }); diff --git a/frontend/src/components/utilities/checks/OnboardingCheck.ts b/frontend/src/components/utilities/checks/OnboardingCheck.ts index 20bb1cd78..f9b47a210 100644 --- a/frontend/src/components/utilities/checks/OnboardingCheck.ts +++ b/frontend/src/components/utilities/checks/OnboardingCheck.ts @@ -1,5 +1,5 @@ +import { fetchUserAction } from "@app/hooks/api/users/queries"; import getOrganizationUsers from "@app/pages/api/organization/GetOrgUsers"; -import checkUserAction from "@app/pages/api/userActions/checkUserAction"; interface OnboardingCheckProps { setTotalOnboardingActionsDone?: (value: number) => void; @@ -20,25 +20,23 @@ const onboardingCheck = async ({ setUsersInOrg }: OnboardingCheckProps) => { let countActions = 0; - const userActionSlack = await checkUserAction({ - action: "slack_cta_clicked" - }); + const userActionSlack = await fetchUserAction( + "slack_cta_clicked" + ); + if (userActionSlack) { countActions += 1; } if (setHasUserClickedSlack) setHasUserClickedSlack(!!userActionSlack); - const userActionSecrets = await checkUserAction({ - action: "first_time_secrets_pushed" - }); + const userActionSecrets = await fetchUserAction("first_time_secrets_pushed"); + if (userActionSecrets) { countActions += 1; } if (setHasUserPushedSecrets) setHasUserPushedSecrets(!!userActionSecrets); - const userActionIntro = await checkUserAction({ - action: "intro_cta_clicked" - }); + const userActionIntro = await fetchUserAction("intro_cta_clicked"); if (userActionIntro) { countActions += 1; } diff --git a/frontend/src/helpers/project.ts b/frontend/src/helpers/project.ts index 3b3dddf5a..c15baf1ef 100644 --- a/frontend/src/helpers/project.ts +++ b/frontend/src/helpers/project.ts @@ -3,8 +3,8 @@ import crypto from "crypto"; import { encryptAssymmetric } from "@app/components/utilities/cryptography/crypto"; import encryptSecrets from "@app/components/utilities/secrets/encryptSecrets"; import { createSecret } from "@app/hooks/api/secrets/queries"; +import { fetchUserDetails } from "@app/hooks/api/users/queries"; import { createWorkspace } from "@app/hooks/api/workspace/queries"; -import getUser from "@app/pages/api/user/getUser"; import uploadKeys from "@app/pages/api/workspace/uploadKeys"; const secretsToBeAdded = [ @@ -108,7 +108,7 @@ const initProjectHelper = async ({ if (!PRIVATE_KEY) throw new Error("Failed to find private key"); - const user = await getUser(); + const user = await fetchUserDetails(); const { ciphertext, nonce } = encryptAssymmetric({ plaintext: randomBytes, diff --git a/frontend/src/hooks/api/users/index.tsx b/frontend/src/hooks/api/users/index.tsx index a209367e2..e1b9fe402 100644 --- a/frontend/src/hooks/api/users/index.tsx +++ b/frontend/src/hooks/api/users/index.tsx @@ -3,8 +3,10 @@ export { useAddUserToOrg, useAddUserToWs, useCreateAPIKey, + useCreateMyAction, useDeleteAPIKey, useDeleteOrgMembership, + useGetMyActions, useGetMyAPIKeys, useGetMyIp, useGetMySessions, @@ -14,6 +16,6 @@ export { useLogoutUser, useRegisterUserAction, useRevokeMySessions, + useUpdateMfaEnabled, useUpdateOrgUserRole, - useUpdateUserAuthProvider -} from "./queries"; + useUpdateUserAuthProvider} from "./queries"; diff --git a/frontend/src/hooks/api/users/queries.tsx b/frontend/src/hooks/api/users/queries.tsx index 93d8d41de..938abf3aa 100644 --- a/frontend/src/hooks/api/users/queries.tsx +++ b/frontend/src/hooks/api/users/queries.tsx @@ -19,7 +19,8 @@ import { RenameUserDTO, TokenVersion, UpdateOrgUserRoleDTO, - User} from "./types"; + User +} from "./types"; const userKeys = { getUser: ["user"] as const, @@ -27,7 +28,7 @@ const userKeys = { getOrgUsers: (orgId: string) => [{ orgId }, "user"], myIp: ["ip"] as const, myAPIKeys: ["api-keys"] as const, - mySessions: ["sessions"] as const + mySessions: ["sessions"] as const, }; export const fetchUserDetails = async () => { @@ -38,7 +39,7 @@ export const fetchUserDetails = async () => { export const useGetUser = () => useQuery(userKeys.getUser, fetchUserDetails); -const fetchUserAction = async (action: string) => { +export const fetchUserAction = async (action: string) => { const { data } = await apiRequest.get<{ userAction: string }>("/api/v1/user-action", { params: { action @@ -303,4 +304,27 @@ export const useRevokeMySessions = () => { queryClient.invalidateQueries(userKeys.mySessions); } }); +} + +export const useUpdateMfaEnabled = () => { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: async ({ + isMfaEnabled + }: { + isMfaEnabled: boolean; + }) => { + const { data: { user } } = await apiRequest.patch( + "/api/v2/users/me/mfa", + { + isMfaEnabled + } + ); + + return user; + }, + onSuccess() { + queryClient.invalidateQueries(userKeys.getUser); + } + }); } \ No newline at end of file diff --git a/frontend/src/hooks/api/users/types.ts b/frontend/src/hooks/api/users/types.ts index 0c312b2b6..fdef407fa 100644 --- a/frontend/src/hooks/api/users/types.ts +++ b/frontend/src/hooks/api/users/types.ts @@ -9,7 +9,7 @@ export enum AuthProvider { export type User = { createdAt: Date; updatedAt: Date; - email?: string; + email: string; firstName?: string; lastName?: string; authProvider?: AuthProvider; diff --git a/frontend/src/pages/api/user/getUser.ts b/frontend/src/pages/api/user/getUser.ts deleted file mode 100644 index afdd268ba..000000000 --- a/frontend/src/pages/api/user/getUser.ts +++ /dev/null @@ -1,20 +0,0 @@ -import SecurityClient from "@app/components/utilities/SecurityClient"; - -/** - * This route gets the information about a specific user. - */ -const getUser = () => - SecurityClient.fetchCall("/api/v1/user", { - method: "GET", - headers: { - "Content-Type": "application/json" - } - }).then(async (res) => { - if (res?.status === 200) { - return (await res.json()).user; - } - console.log("Failed to get user info"); - return undefined; - }); - -export default getUser; diff --git a/frontend/src/pages/api/user/updateMyMfaEnabled.ts b/frontend/src/pages/api/user/updateMyMfaEnabled.ts deleted file mode 100644 index e22f14958..000000000 --- a/frontend/src/pages/api/user/updateMyMfaEnabled.ts +++ /dev/null @@ -1,32 +0,0 @@ -import SecurityClient from "@app/components/utilities/SecurityClient"; - -interface Props { - isMfaEnabled: boolean; -} - -/** - * Update the user's MFA-enabled status to [isMfaEnabled] - * @param {Object} obj - * @param {Boolean} obj.isMfaEnabled - whether or not MFA status should be set to enabled or not - * @returns {User} user - user with updated MFA-enabled status - */ -const updateMyMfaEnabled = async ({ - isMfaEnabled -}: Props) => - SecurityClient.fetchCall("/api/v2/users/me/mfa", { - method: "PATCH", - headers: { - "Content-Type": "application/json" - }, - body: JSON.stringify({ - isMfaEnabled, - }) - }).then(async (res) => { - if (res && res.status === 200) { - return (await res.json()).user; - } - console.log("Failed to update MFA status"); - return undefined; - }); - -export default updateMyMfaEnabled; \ No newline at end of file diff --git a/frontend/src/pages/api/userActions/checkUserAction.ts b/frontend/src/pages/api/userActions/checkUserAction.ts deleted file mode 100644 index 5663c2649..000000000 --- a/frontend/src/pages/api/userActions/checkUserAction.ts +++ /dev/null @@ -1,29 +0,0 @@ -import SecurityClient from "@app/components/utilities/SecurityClient"; - -/** - * This route registers a certain action for a user - * @param {*} email - * @param {*} workspaceId - * @returns - */ -const checkUserAction = ({ action }: { action: string }) => - SecurityClient.fetchCall( - "/api/v1/user-action" + - `?${new URLSearchParams({ - action - })}`, - { - method: "GET", - headers: { - "Content-Type": "application/json" - } - } - ).then(async (res) => { - if (res && res.status === 200) { - return (await res.json()).userAction; - } - console.log("Failed to check a user action"); - return undefined; - }); - -export default checkUserAction; diff --git a/frontend/src/pages/api/userActions/registerUserAction.ts b/frontend/src/pages/api/userActions/registerUserAction.ts deleted file mode 100644 index dd29b7f29..000000000 --- a/frontend/src/pages/api/userActions/registerUserAction.ts +++ /dev/null @@ -1,25 +0,0 @@ -import SecurityClient from "@app/components/utilities/SecurityClient"; - -/** - * This route registers a certain action for a user - * @param {*} action - * @returns - */ -const registerUserAction = ({ action }: { action: string }) => - SecurityClient.fetchCall("/api/v1/user-action", { - method: "POST", - headers: { - "Content-Type": "application/json" - }, - body: JSON.stringify({ - action - }) - }).then(async (res) => { - if (res && res.status === 200) { - return res; - } - console.log("Failed to register a user action"); - return undefined; - }); - -export default registerUserAction; diff --git a/frontend/src/pages/api/workspace/getAWorkspace.ts b/frontend/src/pages/api/workspace/getAWorkspace.ts deleted file mode 100644 index cf4a3d696..000000000 --- a/frontend/src/pages/api/workspace/getAWorkspace.ts +++ /dev/null @@ -1,30 +0,0 @@ -import SecurityClient from "@app/components/utilities/SecurityClient"; - -interface Workspace { - __v: number; - _id: string; - name: string; - organization: string; - environments: Array<{ name: string; slug: string }>; -} - -/** - * This route lets us get the workspaces of a certain user - * @returns - */ -const getAWorkspace = (workspaceID: string) => - SecurityClient.fetchCall(`/api/v1/workspace/${workspaceID}`, { - method: "GET", - headers: { - "Content-Type": "application/json" - } - }).then(async (res) => { - if (res?.status === 200) { - const data = (await res.json()) as unknown as { workspace: Workspace }; - return data.workspace; - } - - throw new Error("Failed to get workspace"); - }); - -export default getAWorkspace; diff --git a/frontend/src/pages/api/workspace/getProjectInfo.ts b/frontend/src/pages/api/workspace/getProjectInfo.ts deleted file mode 100644 index cb1e54c04..000000000 --- a/frontend/src/pages/api/workspace/getProjectInfo.ts +++ /dev/null @@ -1,22 +0,0 @@ -import SecurityClient from "@app/components/utilities/SecurityClient"; - -/** - * This route lets us get the information of a certain project. - * @param {*} projectId - project ID (we renamed workspaces to projects in the app) - * @returns - */ -const getProjectInfo = ({ projectId }: { projectId: string }) => - SecurityClient.fetchCall(`/api/v1/workspace/${projectId}`, { - method: "GET", - headers: { - "Content-Type": "application/json" - } - }).then(async (res) => { - if (res?.status === 200) { - return (await res.json()).workspace; - } - console.log("Failed to get project info"); - return undefined; - }); - -export default getProjectInfo; diff --git a/frontend/src/pages/api/workspace/getWorkspaceEnvironments.ts b/frontend/src/pages/api/workspace/getWorkspaceEnvironments.ts deleted file mode 100644 index 2d7d9359b..000000000 --- a/frontend/src/pages/api/workspace/getWorkspaceEnvironments.ts +++ /dev/null @@ -1,22 +0,0 @@ -import SecurityClient from "@app/components/utilities/SecurityClient"; - -/** - * This route lets us get the environments that a certain user has acess to in a certain project - * @param {string} workspaceId - * @returns - */ -const getWorkspaceEnvironments = ({ workspaceId }: { workspaceId: string }) => - SecurityClient.fetchCall(`/api/v2/workspace/${workspaceId}/environments`, { - method: "GET", - headers: { - "Content-Type": "application/json" - } - }).then(async (res) => { - if (res?.status === 200) { - return (await res.json()).accessibleEnvironments; - } - console.log("Failed to get accessible environments"); - return undefined; - }); - -export default getWorkspaceEnvironments; diff --git a/frontend/src/pages/org/[id]/overview/index.tsx b/frontend/src/pages/org/[id]/overview/index.tsx index 1957d29f5..a026b4da3 100644 --- a/frontend/src/pages/org/[id]/overview/index.tsx +++ b/frontend/src/pages/org/[id]/overview/index.tsx @@ -36,11 +36,10 @@ import { } from "@app/components/v2"; import { TabsObject } from "@app/components/v2/Tabs"; import { useSubscription, useUser, useWorkspace } from "@app/context"; -import { fetchOrgUsers, useAddUserToWs, useCreateWorkspace, useUploadWsKey } from "@app/hooks/api"; +import { fetchOrgUsers, useAddUserToWs, useCreateWorkspace, useRegisterUserAction,useUploadWsKey } from "@app/hooks/api"; import { usePopUp } from "@app/hooks/usePopUp"; import { encryptAssymmetric } from "../../../../components/utilities/cryptography/crypto"; -import registerUserAction from "../../../api/userActions/registerUserAction"; const features = [ { @@ -70,6 +69,7 @@ const LearningItem = ({ userAction, link }: ItemProps): JSX.Element => { + const registerUserAction = useRegisterUserAction(); if (link) { return ( { if (userAction && userAction !== "first_time_secrets_pushed") { - await registerUserAction({ - action: userAction - }); + await registerUserAction.mutateAsync( + userAction + ); } }} className={`group relative flex h-[5.5rem] w-full items-center justify-between overflow-hidden rounded-md border ${ @@ -130,9 +130,7 @@ const LearningItem = ({ tabIndex={0} onClick={async () => { if (userAction) { - await registerUserAction({ - action: userAction - }); + await registerUserAction.mutateAsync(userAction); } }} className="relative my-1.5 flex h-[5.5rem] w-full cursor-pointer items-center justify-between overflow-hidden rounded-md border border-dashed border-bunker-400 bg-bunker-700 py-2 pl-2 pr-6 shadow-xl duration-200 hover:bg-bunker-500" @@ -169,6 +167,7 @@ const LearningItemSquare = ({ userAction, link }: ItemProps): JSX.Element => { + const registerUserAction = useRegisterUserAction(); return ( { if (userAction && userAction !== "first_time_secrets_pushed") { - await registerUserAction({ - action: userAction - }); + await registerUserAction.mutateAsync(userAction); } }} className={`group relative flex w-full items-center justify-between overflow-hidden rounded-md border ${ diff --git a/frontend/src/pages/project/[id]/members/index.tsx b/frontend/src/pages/project/[id]/members/index.tsx index 471c1bca7..cbd13d208 100644 --- a/frontend/src/pages/project/[id]/members/index.tsx +++ b/frontend/src/pages/project/[id]/members/index.tsx @@ -11,14 +11,13 @@ import AddProjectMemberDialog from "@app/components/basic/dialog/AddProjectMembe import ProjectUsersTable from "@app/components/basic/table/ProjectUsersTable"; import guidGenerator from "@app/components/utilities/randomId"; import { Input } from "@app/components/v2"; +import { useGetUser } from "@app/hooks/api"; import { decryptAssymmetric, encryptAssymmetric } from "../../../../components/utilities/cryptography/crypto"; import getOrganizationUsers from "../../../api/organization/GetOrgUsers"; -import getUser from "../../../api/user/getUser"; -// import DeleteUserDialog from '@app/components/basic/dialog/DeleteUserDialog'; import addUserToWorkspace from "../../../api/workspace/addUserToWorkspace"; import getWorkspaceUsers from "../../../api/workspace/getWorkspaceUsers"; import uploadKeys from "../../../api/workspace/uploadKeys"; @@ -43,6 +42,7 @@ interface MembershipProps { // #TODO: Update all the workspaceIds export default function Users() { + const { data: user } = useGetUser(); const [isAddOpen, setIsAddOpen] = useState(false); // let [isDeleteOpen, setIsDeleteOpen] = useState(false); // let [userIdToBeDeleted, setUserIdToBeDeleted] = useState(false); @@ -60,46 +60,47 @@ export default function Users() { const [orgUserList, setOrgUserList] = useState([]); useEffect(() => { - (async () => { - const user = await getUser(); - setPersonalEmail(user.email); + if (user) { + (async () => { + setPersonalEmail(user.email); - // This part quiries the current users of a project - const workspaceUsers = await getWorkspaceUsers({ - workspaceId - }); - const tempUserList = workspaceUsers.map((membership: MembershipProps) => ({ - key: guidGenerator(), - firstName: membership.user?.firstName, - lastName: membership.user?.lastName, - email: membership.user?.email === null ? membership.inviteEmail : membership.user?.email, - role: membership?.role, - status: membership?.status, - userId: membership.user?._id, - membershipId: membership._id, - deniedPermissions: membership.deniedPermissions, - publicKey: membership.user?.publicKey - })); - setUserList(tempUserList); + // This part quiries the current users of a project + const workspaceUsers = await getWorkspaceUsers({ + workspaceId + }); + const tempUserList = workspaceUsers.map((membership: MembershipProps) => ({ + key: guidGenerator(), + firstName: membership.user?.firstName, + lastName: membership.user?.lastName, + email: membership.user?.email === null ? membership.inviteEmail : membership.user?.email, + role: membership?.role, + status: membership?.status, + userId: membership.user?._id, + membershipId: membership._id, + deniedPermissions: membership.deniedPermissions, + publicKey: membership.user?.publicKey + })); + setUserList(tempUserList); - setIsUserListLoading(false); + setIsUserListLoading(false); - // This is needed to know wha users from an org (if any), we are able to add to a certain project - const orgUsers = await getOrganizationUsers({ - orgId: String(localStorage.getItem("orgData.id")) - }); - setOrgUserList(orgUsers); - setEmail( - orgUsers - ?.filter((membership: MembershipProps) => membership.status === "accepted") - .map((membership: MembershipProps) => membership.user.email) - .filter( - (usEmail: string) => - !tempUserList?.map((user1: UserProps) => user1.email).includes(usEmail) - )[0] - ); - })(); - }, []); + // This is needed to know wha users from an org (if any), we are able to add to a certain project + const orgUsers = await getOrganizationUsers({ + orgId: String(localStorage.getItem("orgData.id")) + }); + setOrgUserList(orgUsers); + setEmail( + orgUsers + ?.filter((membership: MembershipProps) => membership.status === "accepted") + .map((membership: MembershipProps) => membership.user.email) + .filter( + (usEmail: string) => + !tempUserList?.map((user1: UserProps) => user1.email).includes(usEmail) + )[0] + ); + })(); + } + }, [user]); const closeAddModal = () => { setIsAddOpen(false); diff --git a/frontend/src/views/Settings/PersonalSettingsPage/SecuritySection/MFASection.tsx b/frontend/src/views/Settings/PersonalSettingsPage/SecuritySection/MFASection.tsx index 8f9b36db7..6f52b7427 100644 --- a/frontend/src/views/Settings/PersonalSettingsPage/SecuritySection/MFASection.tsx +++ b/frontend/src/views/Settings/PersonalSettingsPage/SecuritySection/MFASection.tsx @@ -1,17 +1,14 @@ -import { useEffect, useState } from "react"; - import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider"; import { Checkbox, EmailServiceSetupModal } from "@app/components/v2"; +import { + useGetUser, + useUpdateMfaEnabled} from "@app/hooks/api"; import { useFetchServerStatus } from "@app/hooks/api/serverDetails"; import { usePopUp } from "@app/hooks/usePopUp"; -import { useGetUser } from "../../../../hooks/api"; -import { User } from "../../../../hooks/api/types"; -import updateMyMfaEnabled from "../../../../pages/api/user/updateMyMfaEnabled"; - export const MFASection = () => { - const [isMfaEnabled, setIsMfaEnabled] = useState(false); const { data: user } = useGetUser(); + const { mutateAsync } = useUpdateMfaEnabled(); const { createNotification } = useNotificationContext(); const { handlePopUpToggle, popUp, handlePopUpOpen } = usePopUp([ "setUpEmail" @@ -19,22 +16,12 @@ export const MFASection = () => { const {data: serverDetails } = useFetchServerStatus() - useEffect(() => { - if (user && typeof user.isMfaEnabled !== "undefined") { - setIsMfaEnabled(user.isMfaEnabled); - } - }, [user]); - const toggleMfa = async (state: boolean) => { try { - const newUser: User = await updateMyMfaEnabled({ + const newUser = await mutateAsync({ isMfaEnabled: state }); - if (newUser) { - setIsMfaEnabled(newUser.isMfaEnabled); - } - createNotification({ text: `${newUser.isMfaEnabled ? "Successfully turned on two-factor authentication." : "Successfully turned off two-factor authentication."}`, type: "success" @@ -55,20 +42,22 @@ export const MFASection = () => {

Two-factor Authentication

- { - if (serverDetails?.emailConfigured){ - toggleMfa(state as boolean); - } else { - handlePopUpOpen("setUpEmail"); - } - }} - > - Enable 2-factor authentication via your personal email. - + {user && ( + { + if (serverDetails?.emailConfigured){ + toggleMfa(state as boolean); + } else { + handlePopUpOpen("setUpEmail"); + } + }} + > + Enable 2-factor authentication via your personal email. + + )}