Procházet zdrojové kódy

feat: add backend handling for unarchiving all chats

The previous implementation for unarchiving all chats in `ArchivedChatsModal.svelte` was inefficient, as it sent a separate request for each chat, which could potentially overload the server.

This commit introduces a new backend endpoint, `/chats/unarchive/all`, to handle the bulk unarchiving of all chats for a user with a single API call.

The frontend has been updated to use this new endpoint, resolving the performance issue by minimizing the number of requests to the server.
silentoplayz před 1 týdnem
rodič
revize
a572cf4842

+ 9 - 0
backend/open_webui/models/chats.py

@@ -366,6 +366,15 @@ class ChatTable:
         except Exception:
             return False
 
+    def unarchive_all_chats_by_user_id(self, user_id: str) -> bool:
+        try:
+            with get_db() as db:
+                db.query(Chat).filter_by(user_id=user_id).update({"archived": False})
+                db.commit()
+                return True
+        except Exception:
+            return False
+
     def update_chat_share_id_by_id(
         self, id: str, share_id: Optional[str]
     ) -> Optional[ChatModel]:

+ 10 - 0
backend/open_webui/routers/chats.py

@@ -361,6 +361,16 @@ async def archive_all_chats(user=Depends(get_verified_user)):
     return Chats.archive_all_chats_by_user_id(user.id)
 
 
+############################
+# UnarchiveAllChats
+############################
+
+
+@router.post("/unarchive/all", response_model=bool)
+async def unarchive_all_chats(user=Depends(get_verified_user)):
+    return Chats.unarchive_all_chats_by_user_id(user.id)
+
+
 ############################
 # GetSharedChatById
 ############################

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

@@ -33,6 +33,38 @@ export const createNewChat = async (token: string, chat: object, folderId: strin
 	return res;
 };
 
+export const unarchiveAllChats = async (token: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/chats/unarchive/all`, {
+		method: 'POST',
+		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.detail;
+
+			console.error(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
 export const importChat = async (
 	token: string,
 	chat: object,

+ 26 - 9
src/lib/components/layout/ArchivedChatsModal.svelte

@@ -1,19 +1,26 @@
-<script>
+<script lang="ts">
 	import fileSaver from 'file-saver';
 	const { saveAs } = fileSaver;
 
 	import { toast } from 'svelte-sonner';
 	import { getContext } from 'svelte';
-	import { archiveChatById, getAllArchivedChats, getArchivedChatList } from '$lib/apis/chats';
+	import {
+		archiveChatById,
+		getAllArchivedChats,
+		getArchivedChatList,
+		unarchiveAllChats
+	} from '$lib/apis/chats';
 
 	import ChatsModal from './ChatsModal.svelte';
 	import UnarchiveAllConfirmDialog from '$lib/components/common/ConfirmDialog.svelte';
+	import Spinner from '../common/Spinner.svelte';
 
 	const i18n = getContext('i18n');
 
 	export let show = false;
 	export let onUpdate = () => {};
 
+	let loading = false;
 	let chatList = null;
 	let page = 1;
 
@@ -105,13 +112,17 @@
 	};
 
 	const unarchiveAllHandler = async () => {
-		const chats = await getAllArchivedChats(localStorage.token);
-		for (const chat of chats) {
-			await archiveChatById(localStorage.token, chat.id);
+		loading = true;
+		try {
+			await unarchiveAllChats(localStorage.token);
+			toast.success($i18n.t('All chats have been unarchived.'));
+			onUpdate();
+			await init();
+		} catch (error) {
+			toast.error(`${error}`);
+		} finally {
+			loading = false;
 		}
-
-		onUpdate();
-		init();
 	};
 
 	const init = async () => {
@@ -152,15 +163,21 @@
 		<div class="flex flex-wrap text-sm font-medium gap-1.5 mt-2 m-1 justify-end w-full">
 			<button
 				class=" px-3.5 py-1.5 font-medium hover:bg-black/5 dark:hover:bg-white/5 outline outline-1 outline-gray-100 dark:outline-gray-800 rounded-3xl"
+				disabled={loading}
 				on:click={() => {
 					showUnarchiveAllConfirmDialog = true;
 				}}
 			>
-				{$i18n.t('Unarchive All Archived Chats')}
+				{#if loading}
+					<Spinner className="size-4" />
+				{:else}
+					{$i18n.t('Unarchive All Archived Chats')}
+				{/if}
 			</button>
 
 			<button
 				class="px-3.5 py-1.5 font-medium hover:bg-black/5 dark:hover:bg-white/5 outline outline-1 outline-gray-100 dark:outline-gray-800 rounded-3xl"
+				disabled={loading}
 				on:click={() => {
 					exportChatsHandler();
 				}}