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.
This commit is contained in:
silentoplayz 2025-09-28 13:25:34 -04:00
parent 4aa41aa139
commit a572cf4842
4 changed files with 77 additions and 9 deletions

View file

@ -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]:

View file

@ -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
############################

View file

@ -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,

View file

@ -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();
init();
await init();
} catch (error) {
toast.error(`${error}`);
} finally {
loading = false;
}
};
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;
}}
>
{#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();
}}