diff --git a/backend/open_webui/apps/webui/models/chats.py b/backend/open_webui/apps/webui/models/chats.py index 90e14219b..62e4d2755 100644 --- a/backend/open_webui/apps/webui/models/chats.py +++ b/backend/open_webui/apps/webui/models/chats.py @@ -561,6 +561,21 @@ class ChatTable: all_chats = query.all() return [ChatModel.model_validate(chat) for chat in all_chats] + def get_chats_by_folder_ids_and_user_id( + self, folder_ids: list[str], user_id: str + ) -> list[ChatModel]: + with get_db() as db: + query = db.query(Chat).filter( + Chat.folder_id.in_(folder_ids), Chat.user_id == user_id + ) + query = query.filter(or_(Chat.pinned == False, Chat.pinned == None)) + query = query.filter_by(archived=False) + + query = query.order_by(Chat.updated_at.desc()) + + all_chats = query.all() + return [ChatModel.model_validate(chat) for chat in all_chats] + def update_chat_folder_id_by_id_and_user_id( self, id: str, user_id: str, folder_id: str ) -> Optional[ChatModel]: diff --git a/backend/open_webui/apps/webui/models/folders.py b/backend/open_webui/apps/webui/models/folders.py index a7b919c8e..90e8880aa 100644 --- a/backend/open_webui/apps/webui/models/folders.py +++ b/backend/open_webui/apps/webui/models/folders.py @@ -99,6 +99,30 @@ class FolderTable: except Exception: return None + def get_children_folders_by_id_and_user_id( + self, id: str, user_id: str + ) -> Optional[FolderModel]: + try: + with get_db() as db: + folders = [] + + def get_children(folder): + children = self.get_folders_by_parent_id_and_user_id( + folder.id, user_id + ) + for child in children: + get_children(child) + folders.append(child) + + folder = db.query(Folder).filter_by(id=id, user_id=user_id).first() + if not folder: + return None + + get_children(folder) + return folders + except Exception: + return None + def get_folders_by_user_id(self, user_id: str) -> list[FolderModel]: with get_db() as db: return [ diff --git a/backend/open_webui/apps/webui/routers/chats.py b/backend/open_webui/apps/webui/routers/chats.py index 2cc94e803..515afdc51 100644 --- a/backend/open_webui/apps/webui/routers/chats.py +++ b/backend/open_webui/apps/webui/routers/chats.py @@ -10,6 +10,7 @@ from open_webui.apps.webui.models.chats import ( ChatTitleIdResponse, ) from open_webui.apps.webui.models.tags import TagModel, Tags +from open_webui.apps.webui.models.folders import Folders from open_webui.config import ENABLE_ADMIN_CHAT_ACCESS, ENABLE_ADMIN_EXPORT from open_webui.constants import ERROR_MESSAGES @@ -151,6 +152,26 @@ async def search_user_chats( return chat_list +############################ +# GetChatsByFolderId +############################ + + +@router.get("/folder/{folder_id}", response_model=list[ChatResponse]) +async def get_chats_by_folder_id(folder_id: str, user=Depends(get_verified_user)): + folder_ids = [folder_id] + children_folders = Folders.get_children_folders_by_id_and_user_id( + folder_id, user.id + ) + if children_folders: + folder_ids.extend([folder.id for folder in children_folders]) + + return [ + ChatResponse(**chat.model_dump()) + for chat in Chats.get_chats_by_folder_ids_and_user_id(folder_ids, user.id) + ] + + ############################ # GetPinnedChats ############################ diff --git a/src/lib/apis/chats/index.ts b/src/lib/apis/chats/index.ts index 745672b40..a99ac67e7 100644 --- a/src/lib/apis/chats/index.ts +++ b/src/lib/apis/chats/index.ts @@ -243,6 +243,37 @@ export const getChatListBySearchText = async (token: string, text: string, page: })); }; +export const getChatsByFolderId = async (token: string, folderId: string) => { + let error = null; + + const res = await fetch(`${WEBUI_API_BASE_URL}/chats/folder/${folderId}`, { + method: 'GET', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + ...(token && { authorization: `Bearer ${token}` }) + } + }) + .then(async (res) => { + if (!res.ok) throw await res.json(); + return res.json(); + }) + .then((json) => { + return json; + }) + .catch((err) => { + error = err; + console.log(err); + return null; + }); + + if (error) { + throw error; + } + + return res; +}; + export const getAllArchivedChats = async (token: string) => { let error = null; diff --git a/src/lib/components/icons/ChatMenu.svelte b/src/lib/components/icons/ChatMenu.svelte deleted file mode 100644 index 673e643b1..000000000 --- a/src/lib/components/icons/ChatMenu.svelte +++ /dev/null @@ -1,124 +0,0 @@ - - - { - if (e.detail === false) { - onClose(); - } - }} -> - - - - -
- - { - pinHandler(); - }} - > - -
{$i18n.t('Pin')}
-
- - { - renameHandler(); - }} - > - -
{$i18n.t('Rename')}
-
- - { - cloneChatHandler(); - }} - > - -
{$i18n.t('Clone')}
-
- - { - archiveChatHandler(); - }} - > - -
{$i18n.t('Archive')}
-
- - { - shareHandler(); - }} - > - -
{$i18n.t('Share')}
-
- - { - deleteHandler(); - }} - > - -
{$i18n.t('Delete')}
-
- -
- -
- { - show = false; - onClose(); - }} - /> -
-
-
-
diff --git a/src/lib/components/icons/Download.svelte b/src/lib/components/icons/Download.svelte new file mode 100644 index 000000000..55620e9fe --- /dev/null +++ b/src/lib/components/icons/Download.svelte @@ -0,0 +1,19 @@ + + + + + diff --git a/src/lib/components/layout/Sidebar/ChatMenu.svelte b/src/lib/components/layout/Sidebar/ChatMenu.svelte index e983818cd..9ed152e67 100644 --- a/src/lib/components/layout/Sidebar/ChatMenu.svelte +++ b/src/lib/components/layout/Sidebar/ChatMenu.svelte @@ -26,6 +26,7 @@ import { chats } from '$lib/stores'; import { createMessagesList } from '$lib/utils'; import { downloadChatAsPDF } from '$lib/apis/utils'; + import Download from '$lib/components/icons/Download.svelte'; const i18n = getContext('i18n'); @@ -198,20 +199,7 @@ - - - +
{$i18n.t('Download')}
diff --git a/src/lib/components/layout/Sidebar/Folders/FolderMenu.svelte b/src/lib/components/layout/Sidebar/Folders/FolderMenu.svelte index 12cebfe6b..ce0d5ac58 100644 --- a/src/lib/components/layout/Sidebar/Folders/FolderMenu.svelte +++ b/src/lib/components/layout/Sidebar/Folders/FolderMenu.svelte @@ -10,6 +10,7 @@ import GarbageBin from '$lib/components/icons/GarbageBin.svelte'; import Pencil from '$lib/components/icons/Pencil.svelte'; import Tooltip from '$lib/components/common/Tooltip.svelte'; + import Download from '$lib/components/icons/Download.svelte'; let show = false; @@ -44,6 +45,17 @@
{$i18n.t('Rename')}
+ { + dispatch('export'); + }} + > + + +
{$i18n.t('Export')}
+
+ { diff --git a/src/lib/components/layout/Sidebar/RecursiveFolder.svelte b/src/lib/components/layout/Sidebar/RecursiveFolder.svelte index 1185ac17d..2779d53ae 100644 --- a/src/lib/components/layout/Sidebar/RecursiveFolder.svelte +++ b/src/lib/components/layout/Sidebar/RecursiveFolder.svelte @@ -1,10 +1,13 @@ { showDeleteConfirm = true; }} + on:export={() => { + exportHandler(); + }} >