浏览代码

feat: export all chats from db

admin only
Timothy J. Baek 1 年之前
父节点
当前提交
7c2f297c84

+ 9 - 3
backend/apps/web/models/chats.py

@@ -70,9 +70,9 @@ class ChatTable:
             **{
                 "id": id,
                 "user_id": user_id,
-                "title": form_data.chat["title"]
-                if "title" in form_data.chat
-                else "New Chat",
+                "title": (
+                    form_data.chat["title"] if "title" in form_data.chat else "New Chat"
+                ),
                 "chat": json.dumps(form_data.chat),
                 "timestamp": int(time.time()),
             }
@@ -131,6 +131,12 @@ class ChatTable:
             .order_by(Chat.timestamp.desc())
         ]
 
+    def get_all_chats(self) -> List[ChatModel]:
+        return [
+            ChatModel(**model_to_dict(chat))
+            for chat in Chat.select().order_by(Chat.timestamp.desc())
+        ]
+
     def get_all_chats_by_user_id(self, user_id: str) -> List[ChatModel]:
         return [
             ChatModel(**model_to_dict(chat))

+ 19 - 0
backend/apps/web/routers/chats.py

@@ -54,6 +54,25 @@ async def get_all_user_chats(user=Depends(get_current_user)):
     ]
 
 
+############################
+# GetAllChatsInDB
+############################
+
+
+@router.get("/all/db", response_model=List[ChatResponse])
+async def get_all_user_chats_in_db(user=Depends(get_current_user)):
+    if user.role == "admin":
+        return [
+            ChatResponse(**{**chat.model_dump(), "chat": json.loads(chat.chat)})
+            for chat in Chats.get_all_chats()
+        ]
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_403_FORBIDDEN,
+            detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
+        )
+
+
 ############################
 # CreateNewChat
 ############################

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

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

+ 43 - 5
src/lib/components/chat/SettingsModal.svelte

@@ -17,7 +17,13 @@
 		deleteModel
 	} from '$lib/apis/ollama';
 	import { updateUserPassword } from '$lib/apis/auths';
-	import { createNewChat, deleteAllChats, getAllChats, getChatList } from '$lib/apis/chats';
+	import {
+		createNewChat,
+		deleteAllChats,
+		getAllChats,
+		getAllUserChats,
+		getChatList
+	} from '$lib/apis/chats';
 	import { WEB_UI_VERSION, WEBUI_API_BASE_URL } from '$lib/constants';
 
 	import { config, models, voices, settings, user, chats } from '$lib/stores';
@@ -179,6 +185,13 @@
 		saveAs(blob, `chat-export-${Date.now()}.json`);
 	};
 
+	const exportAllUserChats = async () => {
+		let blob = new Blob([JSON.stringify(await getAllUserChats(localStorage.token))], {
+			type: 'application/json'
+		});
+		saveAs(blob, `all-chats-export-${Date.now()}.json`);
+	};
+
 	const deleteChats = async () => {
 		await goto('/');
 		await deleteAllChats(localStorage.token);
@@ -1954,6 +1967,33 @@
 									on:click={() => {
 										showDeleteConfirm = true;
 									}}
+								>
+									<div class=" self-center mr-3">
+										<svg
+											xmlns="http://www.w3.org/2000/svg"
+											viewBox="0 0 16 16"
+											fill="currentColor"
+											class="w-4 h-4"
+										>
+											<path
+												fill-rule="evenodd"
+												d="M4 2a1.5 1.5 0 0 0-1.5 1.5v9A1.5 1.5 0 0 0 4 14h8a1.5 1.5 0 0 0 1.5-1.5V6.621a1.5 1.5 0 0 0-.44-1.06L9.94 2.439A1.5 1.5 0 0 0 8.878 2H4Zm7 7a.75.75 0 0 1-.75.75h-4.5a.75.75 0 0 1 0-1.5h4.5A.75.75 0 0 1 11 9Z"
+												clip-rule="evenodd"
+											/>
+										</svg>
+									</div>
+									<div class=" self-center text-sm font-medium">Delete Chats</div>
+								</button>
+							{/if}
+
+							{#if $user?.role === 'admin'}
+								<hr class=" dark:border-gray-700" />
+
+								<button
+									class=" flex rounded-md py-2 px-3.5 w-full hover:bg-gray-200 dark:hover:bg-gray-800 transition"
+									on:click={() => {
+										exportAllUserChats();
+									}}
 								>
 									<div class=" self-center mr-3">
 										<svg
@@ -1967,16 +2007,14 @@
 											/>
 											<path
 												fill-rule="evenodd"
-												d="M13 6H3v6a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V6ZM5.72 7.47a.75.75 0 0 1 1.06 0L8 8.69l1.22-1.22a.75.75 0 1 1 1.06 1.06L9.06 9.75l1.22 1.22a.75.75 0 1 1-1.06 1.06L8 10.81l-1.22 1.22a.75.75 0 0 1-1.06-1.06l1.22-1.22-1.22-1.22a.75.75 0 0 1 0-1.06Z"
+												d="M13 6H3v6a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V6ZM8.75 7.75a.75.75 0 0 0-1.5 0v2.69L6.03 9.22a.75.75 0 0 0-1.06 1.06l2.5 2.5a.75.75 0 0 0 1.06 0l2.5-2.5a.75.75 0 1 0-1.06-1.06l-1.22 1.22V7.75Z"
 												clip-rule="evenodd"
 											/>
 										</svg>
 									</div>
-									<div class=" self-center text-sm font-medium">Delete All Chats</div>
+									<div class=" self-center text-sm font-medium">Export All Chats (All Users)</div>
 								</button>
-							{/if}
 
-							{#if $user?.role === 'admin'}
 								<hr class=" dark:border-gray-700" />
 
 								<button