mirror of
https://github.com/StanGirard/quivr.git
synced 2024-11-23 04:17:48 +03:00
feat(user): Delete User Data from frontend (#2476)
# Description Please include a summary of the changes and the related issue. Please also include relevant motivation and context. ## Checklist before requesting a review Please delete options that are not relevant. - [ ] My code follows the style guidelines of this project - [ ] I have performed a self-review of my code - [ ] I have commented hard-to-understand areas - [ ] I have ideally added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged ## Screenshots (if appropriate): --------- Co-authored-by: Stan Girard <girard.stanislas@gmail.com>
This commit is contained in:
parent
699097f6ac
commit
8d54187713
@ -79,6 +79,26 @@ def get_user_identity_route(
|
||||
"""
|
||||
return user_repository.get_user_identity(current_user.id)
|
||||
|
||||
@user_router.delete(
|
||||
"/user_data",
|
||||
dependencies=[Depends(AuthBearer())],
|
||||
tags=["User"],
|
||||
)
|
||||
async def delete_user_data_route(
|
||||
current_user: UserIdentity = Depends(get_current_user),
|
||||
):
|
||||
"""
|
||||
Delete a user.
|
||||
|
||||
- `user_id`: The ID of the user to delete.
|
||||
|
||||
This endpoint deletes a user from the system.
|
||||
"""
|
||||
|
||||
user_repository.delete_user_data(current_user.id)
|
||||
|
||||
return {"message": "User deleted successfully"}
|
||||
|
||||
@user_router.get(
|
||||
"/user/credits",
|
||||
dependencies=[Depends(AuthBearer())],
|
||||
|
@ -76,6 +76,30 @@ class Users(UsersInterface):
|
||||
).execute()
|
||||
return response.data[0]["email"]
|
||||
|
||||
def delete_user_data(self, user_id):
|
||||
response = (
|
||||
self.db.from_("brains_users")
|
||||
.select("brain_id")
|
||||
.filter("rights", "eq", "Owner")
|
||||
.filter("user_id", "eq", str(user_id))
|
||||
.execute()
|
||||
)
|
||||
brain_ids = [row["brain_id"] for row in response.data]
|
||||
|
||||
for brain_id in brain_ids:
|
||||
self.db.table("brains").delete().filter("brain_id", "eq", brain_id).execute()
|
||||
|
||||
for brain_id in brain_ids:
|
||||
self.db.table("brains_vectors").delete().filter("brain_id", "eq", brain_id).execute()
|
||||
|
||||
for brain_id in brain_ids:
|
||||
self.db.table("chat_history").delete().filter("brain_id", "eq", brain_id).execute()
|
||||
|
||||
self.db.table("user_settings").delete().filter("user_id", "eq", str(user_id)).execute()
|
||||
self.db.table("user_identity").delete().filter("user_id", "eq", str(user_id)).execute()
|
||||
self.db.table("users").delete().filter("id", "eq", str(user_id)).execute()
|
||||
|
||||
|
||||
def get_user_credits(self, user_id):
|
||||
user_usage_instance = user_usage.UserUsage(id=user_id)
|
||||
|
||||
|
@ -46,6 +46,15 @@ class UsersInterface(ABC):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def delete_user_data(self, user_id: str):
|
||||
"""
|
||||
Delete a user.
|
||||
|
||||
- `user_id`: The ID of the user to delete.
|
||||
|
||||
This endpoint deletes a user from the system.
|
||||
"""
|
||||
@abstractmethod
|
||||
def get_user_credits(self, user_id: UUID) -> int:
|
||||
"""
|
||||
Get user remaining credits
|
||||
|
@ -7,3 +7,14 @@
|
||||
flex-direction: column;
|
||||
gap: Spacings.$spacing05;
|
||||
}
|
||||
|
||||
.modal_wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: Spacings.$spacing05;
|
||||
|
||||
.buttons {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,9 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { useUserApi } from "@/lib/api/user/useUserApi";
|
||||
import PageHeader from "@/lib/components/PageHeader/PageHeader";
|
||||
import { Modal } from "@/lib/components/ui/Modal/Modal";
|
||||
import QuivrButton from "@/lib/components/ui/QuivrButton/QuivrButton";
|
||||
@ -18,7 +20,10 @@ import { useLogoutModal } from "../../lib/hooks/useLogoutModal";
|
||||
const UserPage = (): JSX.Element => {
|
||||
const { session } = useSupabase();
|
||||
const { userData } = useUserData();
|
||||
const { deleteUserData } = useUserApi();
|
||||
const { t } = useTranslation(["translation", "logout"]);
|
||||
const [deleteAccountModalOpened, setDeleteAccountModalOpened] =
|
||||
useState(false);
|
||||
const {
|
||||
handleLogout,
|
||||
isLoggingOut,
|
||||
@ -26,14 +31,24 @@ const UserPage = (): JSX.Element => {
|
||||
setIsLogoutModalOpened,
|
||||
} = useLogoutModal();
|
||||
|
||||
const button: ButtonType = {
|
||||
label: "Logout",
|
||||
color: "dangerous",
|
||||
onClick: () => {
|
||||
setIsLogoutModalOpened(true);
|
||||
const buttons: ButtonType[] = [
|
||||
{
|
||||
label: "Logout",
|
||||
color: "dangerous",
|
||||
onClick: () => {
|
||||
setIsLogoutModalOpened(true);
|
||||
},
|
||||
iconName: "logout",
|
||||
},
|
||||
iconName: "logout",
|
||||
};
|
||||
{
|
||||
label: "Delete Account",
|
||||
color: "dangerous",
|
||||
onClick: () => {
|
||||
setDeleteAccountModalOpened(true);
|
||||
},
|
||||
iconName: "delete",
|
||||
},
|
||||
];
|
||||
|
||||
if (!session || !userData) {
|
||||
redirectToLogin();
|
||||
@ -42,7 +57,7 @@ const UserPage = (): JSX.Element => {
|
||||
return (
|
||||
<>
|
||||
<div className={styles.page_header}>
|
||||
<PageHeader iconName="user" label="Profile" buttons={[button]} />
|
||||
<PageHeader iconName="user" label="Profile" buttons={buttons} />
|
||||
</div>
|
||||
<div className={styles.user_page_container}>
|
||||
<div className={styles.content_wrapper}>
|
||||
@ -55,11 +70,9 @@ const UserPage = (): JSX.Element => {
|
||||
size="auto"
|
||||
CloseTrigger={<div />}
|
||||
>
|
||||
<div className="text-center flex flex-col items-center gap-5">
|
||||
<h2 className="text-lg font-medium mb-5">
|
||||
{t("areYouSure", { ns: "logout" })}
|
||||
</h2>
|
||||
<div className="flex gap-5 items-center justify-center">
|
||||
<div className={styles.modal_wrapper}>
|
||||
<h2>{t("areYouSure", { ns: "logout" })}</h2>
|
||||
<div className={styles.buttons}>
|
||||
<QuivrButton
|
||||
onClick={() => setIsLogoutModalOpened(false)}
|
||||
color="primary"
|
||||
@ -76,6 +89,34 @@ const UserPage = (): JSX.Element => {
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
<Modal
|
||||
isOpen={deleteAccountModalOpened}
|
||||
setOpen={setDeleteAccountModalOpened}
|
||||
size="auto"
|
||||
CloseTrigger={<div />}
|
||||
>
|
||||
<div className={styles.modal_wrapper}>
|
||||
<h2>Are you sure you want to delete your account ?</h2>
|
||||
<div className={styles.buttons}>
|
||||
<QuivrButton
|
||||
onClick={() => setDeleteAccountModalOpened(false)}
|
||||
color="primary"
|
||||
label={t("cancel", { ns: "logout" })}
|
||||
iconName="close"
|
||||
></QuivrButton>
|
||||
<QuivrButton
|
||||
isLoading={isLoggingOut}
|
||||
color="dangerous"
|
||||
onClick={() => {
|
||||
void deleteUserData();
|
||||
void handleLogout();
|
||||
}}
|
||||
label="Delete Account"
|
||||
iconName="logout"
|
||||
></QuivrButton>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { useAxios } from "@/lib/hooks";
|
||||
|
||||
import {
|
||||
deleteUserData,
|
||||
getUser,
|
||||
getUserCredits,
|
||||
getUserIdentity,
|
||||
@ -18,6 +19,7 @@ export const useUserApi = () => {
|
||||
) => updateUserIdentity(userIdentityUpdatableProperties, axiosInstance),
|
||||
getUserIdentity: async () => getUserIdentity(axiosInstance),
|
||||
getUser: async () => getUser(axiosInstance),
|
||||
deleteUserData: async () => deleteUserData(axiosInstance),
|
||||
getUserCredits: async () => getUserCredits(axiosInstance),
|
||||
};
|
||||
};
|
||||
|
@ -31,7 +31,7 @@ export type UserIdentityUpdatableProperties = {
|
||||
};
|
||||
|
||||
export type UserIdentity = {
|
||||
user_id: UUID;
|
||||
id: UUID;
|
||||
onboarded: boolean;
|
||||
username: string;
|
||||
};
|
||||
@ -54,6 +54,12 @@ export const getUser = async (
|
||||
axiosInstance: AxiosInstance
|
||||
): Promise<UserStats> => (await axiosInstance.get<UserStats>("/user")).data;
|
||||
|
||||
export const deleteUserData = async (
|
||||
axiosInstance: AxiosInstance
|
||||
): Promise<void> => {
|
||||
await axiosInstance.delete(`/user_data`);
|
||||
};
|
||||
|
||||
export const getUserCredits = async (
|
||||
axiosInstance: AxiosInstance
|
||||
): Promise<number> => (await axiosInstance.get<number>("/user/credits")).data;
|
||||
|
@ -30,10 +30,6 @@ export const useLogoutModal = () => {
|
||||
text: t("error", { errorMessage: error.message, ns: "logout" }),
|
||||
});
|
||||
} else {
|
||||
publish({
|
||||
variant: "success",
|
||||
text: t("loggedOut", { ns: "logout" }),
|
||||
});
|
||||
window.location.href = "/";
|
||||
}
|
||||
setIsLoggingOut(false);
|
||||
|
Loading…
Reference in New Issue
Block a user