mirror of
https://github.com/open-webui/open-webui.git
synced 2025-12-12 04:15:25 +00:00
feat/enh: move chats in folder on delete
Co-Authored-By: expruc <25387342+expruc@users.noreply.github.com>
This commit is contained in:
parent
b2034861ae
commit
e6951e804a
5 changed files with 76 additions and 26 deletions
|
|
@ -1142,6 +1142,20 @@ class ChatTable:
|
||||||
except Exception:
|
except Exception:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def move_chats_by_user_id_and_folder_id(
|
||||||
|
self, user_id: str, folder_id: str, new_folder_id: Optional[str]
|
||||||
|
) -> bool:
|
||||||
|
try:
|
||||||
|
with get_db() as db:
|
||||||
|
db.query(Chat).filter_by(user_id=user_id, folder_id=folder_id).update(
|
||||||
|
{"folder_id": new_folder_id}
|
||||||
|
)
|
||||||
|
db.commit()
|
||||||
|
|
||||||
|
return True
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
|
||||||
def delete_shared_chats_by_user_id(self, user_id: str) -> bool:
|
def delete_shared_chats_by_user_id(self, user_id: str) -> bool:
|
||||||
try:
|
try:
|
||||||
with get_db() as db:
|
with get_db() as db:
|
||||||
|
|
|
||||||
|
|
@ -258,7 +258,10 @@ async def update_folder_is_expanded_by_id(
|
||||||
|
|
||||||
@router.delete("/{id}")
|
@router.delete("/{id}")
|
||||||
async def delete_folder_by_id(
|
async def delete_folder_by_id(
|
||||||
request: Request, id: str, user=Depends(get_verified_user)
|
request: Request,
|
||||||
|
id: str,
|
||||||
|
delete_contents: Optional[bool] = True,
|
||||||
|
user=Depends(get_verified_user),
|
||||||
):
|
):
|
||||||
if Chats.count_chats_by_folder_id_and_user_id(id, user.id):
|
if Chats.count_chats_by_folder_id_and_user_id(id, user.id):
|
||||||
chat_delete_permission = has_permission(
|
chat_delete_permission = has_permission(
|
||||||
|
|
@ -277,8 +280,14 @@ async def delete_folder_by_id(
|
||||||
if folder:
|
if folder:
|
||||||
try:
|
try:
|
||||||
folder_ids = Folders.delete_folder_by_id_and_user_id(id, user.id)
|
folder_ids = Folders.delete_folder_by_id_and_user_id(id, user.id)
|
||||||
|
|
||||||
for folder_id in folder_ids:
|
for folder_id in folder_ids:
|
||||||
Chats.delete_chats_by_user_id_and_folder_id(user.id, folder_id)
|
if delete_contents:
|
||||||
|
Chats.delete_chats_by_user_id_and_folder_id(user.id, folder_id)
|
||||||
|
else:
|
||||||
|
Chats.move_chats_by_user_id_and_folder_id(
|
||||||
|
user.id, folder_id, None
|
||||||
|
)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
|
||||||
|
|
@ -239,10 +239,13 @@ export const updateFolderItemsById = async (token: string, id: string, items: Fo
|
||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const deleteFolderById = async (token: string, id: string) => {
|
export const deleteFolderById = async (token: string, id: string, deleteContents: boolean) => {
|
||||||
let error = null;
|
let error = null;
|
||||||
|
|
||||||
const res = await fetch(`${WEBUI_API_BASE_URL}/folders/${id}`, {
|
const searchParams = new URLSearchParams();
|
||||||
|
searchParams.append('delete_contents', deleteContents ? 'true' : 'false');
|
||||||
|
|
||||||
|
const res = await fetch(`${WEBUI_API_BASE_URL}/folders/${id}?${searchParams.toString()}`, {
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
headers: {
|
headers: {
|
||||||
Accept: 'application/json',
|
Accept: 'application/json',
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@
|
||||||
|
|
||||||
let showFolderModal = false;
|
let showFolderModal = false;
|
||||||
let showDeleteConfirm = false;
|
let showDeleteConfirm = false;
|
||||||
|
let deleteFolderContents = true;
|
||||||
|
|
||||||
const updateHandler = async ({ name, meta, data }) => {
|
const updateHandler = async ({ name, meta, data }) => {
|
||||||
if (name === '') {
|
if (name === '') {
|
||||||
|
|
@ -98,10 +99,12 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
const deleteHandler = async () => {
|
const deleteHandler = async () => {
|
||||||
const res = await deleteFolderById(localStorage.token, folder.id).catch((error) => {
|
const res = await deleteFolderById(localStorage.token, folder.id, deleteFolderContents).catch(
|
||||||
toast.error(`${error}`);
|
(error) => {
|
||||||
return null;
|
toast.error(`${error}`);
|
||||||
});
|
return null;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
if (res) {
|
if (res) {
|
||||||
toast.success($i18n.t('Folder deleted successfully'));
|
toast.success($i18n.t('Folder deleted successfully'));
|
||||||
|
|
@ -141,15 +144,22 @@
|
||||||
deleteHandler();
|
deleteHandler();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div class=" text-sm text-gray-700 dark:text-gray-300 flex-1 line-clamp-3">
|
<div class=" text-sm text-gray-700 dark:text-gray-300 flex-1 line-clamp-3 mb-2">
|
||||||
{@html DOMPurify.sanitize(
|
<!-- {$i18n.t('This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.', {
|
||||||
$i18n.t(
|
NAME: folders[folderId].name
|
||||||
'This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.',
|
})} -->
|
||||||
{
|
|
||||||
NAME: folder.name
|
{$i18n.t(`Are you sure you want to delete "{{NAME}}"?`, {
|
||||||
}
|
NAME: folders[folderId].name
|
||||||
)
|
})}
|
||||||
)}
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center gap-1.5">
|
||||||
|
<input type="checkbox" bind:checked={deleteFolderContents} />
|
||||||
|
|
||||||
|
<div class="text-xs text-gray-500">
|
||||||
|
{$i18n.t('Delete all contents inside this folder')}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</DeleteConfirmDialog>
|
</DeleteConfirmDialog>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,8 @@
|
||||||
|
|
||||||
export let className = '';
|
export let className = '';
|
||||||
|
|
||||||
|
export let deleteFolderContents = true;
|
||||||
|
|
||||||
export let parentDragged = false;
|
export let parentDragged = false;
|
||||||
|
|
||||||
export let onDelete = (e) => {};
|
export let onDelete = (e) => {};
|
||||||
|
|
@ -288,10 +290,12 @@
|
||||||
let showDeleteConfirm = false;
|
let showDeleteConfirm = false;
|
||||||
|
|
||||||
const deleteHandler = async () => {
|
const deleteHandler = async () => {
|
||||||
const res = await deleteFolderById(localStorage.token, folderId).catch((error) => {
|
const res = await deleteFolderById(localStorage.token, folderId, deleteFolderContents).catch(
|
||||||
toast.error(`${error}`);
|
(error) => {
|
||||||
return null;
|
toast.error(`${error}`);
|
||||||
});
|
return null;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
if (res) {
|
if (res) {
|
||||||
toast.success($i18n.t('Folder deleted successfully'));
|
toast.success($i18n.t('Folder deleted successfully'));
|
||||||
|
|
@ -430,12 +434,22 @@
|
||||||
deleteHandler();
|
deleteHandler();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div class=" text-sm text-gray-700 dark:text-gray-300 flex-1 line-clamp-3">
|
<div class=" text-sm text-gray-700 dark:text-gray-300 flex-1 line-clamp-3 mb-2">
|
||||||
{@html DOMPurify.sanitize(
|
<!-- {$i18n.t('This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.', {
|
||||||
$i18n.t('This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.', {
|
|
||||||
NAME: folders[folderId].name
|
NAME: folders[folderId].name
|
||||||
})
|
})} -->
|
||||||
)}
|
|
||||||
|
{$i18n.t(`Are you sure you want to delete "{{NAME}}"?`, {
|
||||||
|
NAME: folders[folderId].name
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center gap-1.5">
|
||||||
|
<input type="checkbox" bind:checked={deleteFolderContents} />
|
||||||
|
|
||||||
|
<div class="text-xs text-gray-500">
|
||||||
|
{$i18n.t('Delete all contents inside this folder')}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</DeleteConfirmDialog>
|
</DeleteConfirmDialog>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue