Browse Source

Merge pull request #2749 from open-webui/dev

feat: single chat export
Timothy Jaeryang Baek 11 months ago
parent
commit
4b459e372f

+ 9 - 0
backend/apps/webui/models/chats.py

@@ -298,6 +298,15 @@ class ChatTable:
             # .limit(limit).offset(skip)
             # .limit(limit).offset(skip)
         ]
         ]
 
 
+    def get_archived_chats_by_user_id(self, user_id: str) -> List[ChatModel]:
+        return [
+            ChatModel(**model_to_dict(chat))
+            for chat in Chat.select()
+            .where(Chat.archived == True)
+            .where(Chat.user_id == user_id)
+            .order_by(Chat.updated_at.desc())
+        ]
+
     def delete_chat_by_id(self, id: str) -> bool:
     def delete_chat_by_id(self, id: str) -> bool:
         try:
         try:
             query = Chat.delete().where((Chat.id == id))
             query = Chat.delete().where((Chat.id == id))

+ 13 - 0
backend/apps/webui/routers/chats.py

@@ -113,6 +113,19 @@ async def get_user_chats(user=Depends(get_current_user)):
     ]
     ]
 
 
 
 
+############################
+# GetArchivedChats
+############################
+
+
+@router.get("/all/archived", response_model=List[ChatResponse])
+async def get_user_chats(user=Depends(get_current_user)):
+    return [
+        ChatResponse(**{**chat.model_dump(), "chat": json.loads(chat.chat)})
+        for chat in Chats.get_archived_chats_by_user_id(user.id)
+    ]
+
+
 ############################
 ############################
 # GetAllChatsInDB
 # GetAllChatsInDB
 ############################
 ############################

+ 31 - 0
src/lib/apis/chats/index.ts

@@ -162,6 +162,37 @@ export const getAllChats = async (token: string) => {
 	return res;
 	return res;
 };
 };
 
 
+export const getAllArchivedChats = async (token: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/chats/all/archived`, {
+		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 getAllUserChats = async (token: string) => {
 export const getAllUserChats = async (token: string) => {
 	let error = null;
 	let error = null;
 
 

+ 15 - 0
src/lib/components/layout/Navbar/Menu.svelte

@@ -63,6 +63,13 @@
 		// Revoke the URL to release memory
 		// Revoke the URL to release memory
 		window.URL.revokeObjectURL(url);
 		window.URL.revokeObjectURL(url);
 	};
 	};
+
+	const downloadJSONExport = async () => {
+		let blob = new Blob([JSON.stringify([chat])], {
+			type: 'application/json'
+		});
+		saveAs(blob, `chat-export-${Date.now()}.json`);
+	};
 </script>
 </script>
 
 
 <Dropdown
 <Dropdown
@@ -164,6 +171,14 @@
 					transition={flyAndScale}
 					transition={flyAndScale}
 					sideOffset={8}
 					sideOffset={8}
 				>
 				>
+					<DropdownMenu.Item
+						class="flex gap-2 items-center px-3 py-2 text-sm  cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
+						on:click={() => {
+							downloadJSONExport();
+						}}
+					>
+						<div class="flex items-center line-clamp-1">{$i18n.t('Export chat (.json)')}</div>
+					</DropdownMenu.Item>
 					<DropdownMenu.Item
 					<DropdownMenu.Item
 						class="flex gap-2 items-center px-3 py-2 text-sm  cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
 						class="flex gap-2 items-center px-3 py-2 text-sm  cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
 						on:click={() => {
 						on:click={() => {

+ 7 - 1
src/lib/components/layout/Sidebar/ArchivedChatsModal.svelte

@@ -8,7 +8,12 @@
 	const dispatch = createEventDispatcher();
 	const dispatch = createEventDispatcher();
 
 
 	import Modal from '$lib/components/common/Modal.svelte';
 	import Modal from '$lib/components/common/Modal.svelte';
-	import { archiveChatById, deleteChatById, getArchivedChatList } from '$lib/apis/chats';
+	import {
+		archiveChatById,
+		deleteChatById,
+		getAllArchivedChats,
+		getArchivedChatList
+	} from '$lib/apis/chats';
 	import Tooltip from '$lib/components/common/Tooltip.svelte';
 	import Tooltip from '$lib/components/common/Tooltip.svelte';
 
 
 	const i18n = getContext('i18n');
 	const i18n = getContext('i18n');
@@ -38,6 +43,7 @@
 	};
 	};
 
 
 	const exportChatsHandler = async () => {
 	const exportChatsHandler = async () => {
+		const chats = await getAllArchivedChats(localStorage.token);
 		let blob = new Blob([JSON.stringify(chats)], {
 		let blob = new Blob([JSON.stringify(chats)], {
 			type: 'application/json'
 			type: 'application/json'
 		});
 		});