diff --git a/backend/open_webui/models/chats.py b/backend/open_webui/models/chats.py
index 12359eec9..eb0763048 100644
--- a/backend/open_webui/models/chats.py
+++ b/backend/open_webui/models/chats.py
@@ -168,6 +168,14 @@ class ChatTitleIdResponse(BaseModel):
created_at: int
+class SharedChatResponse(BaseModel):
+ id: str
+ title: str
+ share_id: Optional[str] = None
+ updated_at: int
+ created_at: int
+
+
class ChatListResponse(BaseModel):
items: list[ChatModel]
total: int
@@ -675,6 +683,49 @@ class ChatTable:
all_chats = query.all()
return [ChatModel.model_validate(chat) for chat in all_chats]
+ def get_shared_chat_list_by_user_id(
+ self,
+ user_id: str,
+ filter: Optional[dict] = None,
+ skip: int = 0,
+ limit: int = 50,
+ db: Optional[Session] = None,
+ ) -> list[ChatModel]:
+
+ with get_db_context(db) as db:
+ query = db.query(Chat).filter_by(user_id=user_id).filter(
+ Chat.share_id.isnot(None)
+ )
+
+ if filter:
+ query_key = filter.get("query")
+ if query_key:
+ query = query.filter(Chat.title.ilike(f"%{query_key}%"))
+
+ order_by = filter.get("order_by")
+ direction = filter.get("direction")
+
+ if order_by and direction:
+ if not getattr(Chat, order_by, None):
+ raise ValueError("Invalid order_by field")
+
+ if direction.lower() == "asc":
+ query = query.order_by(getattr(Chat, order_by).asc())
+ elif direction.lower() == "desc":
+ query = query.order_by(getattr(Chat, order_by).desc())
+ else:
+ raise ValueError("Invalid direction for ordering")
+ else:
+ query = query.order_by(Chat.updated_at.desc())
+
+ if skip:
+ query = query.offset(skip)
+ if limit:
+ query = query.limit(limit)
+
+ all_chats = query.all()
+ return [ChatModel.model_validate(chat) for chat in all_chats]
+
def get_chat_list_by_user_id(
self,
user_id: str,
diff --git a/backend/open_webui/routers/chats.py b/backend/open_webui/routers/chats.py
index 885360aad..e03cdc7ba 100644
--- a/backend/open_webui/routers/chats.py
+++ b/backend/open_webui/routers/chats.py
@@ -16,6 +16,7 @@ from open_webui.models.chats import (
ChatResponse,
Chats,
ChatTitleIdResponse,
+ SharedChatResponse,
ChatStatsExport,
AggregateChatStats,
ChatBody,
@@ -858,6 +859,48 @@ async def unarchive_all_chats(
return Chats.unarchive_all_chats_by_user_id(user.id, db=db)
+############################
+# GetSharedChats
+############################
+
+
+@router.get("/shared", response_model=list[SharedChatResponse])
+async def get_shared_session_user_chat_list(
+ page: Optional[int] = None,
+ query: Optional[str] = None,
+ order_by: Optional[str] = None,
+ direction: Optional[str] = None,
+ user=Depends(get_verified_user),
+ db: Session = Depends(get_session),
+):
+ if page is None:
+ page = 1
+
+ limit = 60
+ skip = (page - 1) * limit
+
+ filter = {}
+ if query:
+ filter["query"] = query
+ if order_by:
+ filter["order_by"] = order_by
+ if direction:
+ filter["direction"] = direction
+
+ chat_list = [
+ SharedChatResponse(**chat.model_dump())
+ for chat in Chats.get_shared_chat_list_by_user_id(
+ user.id,
+ filter=filter,
+ skip=skip,
+ limit=limit,
+ db=db,
+ )
+ ]
+
+ return chat_list
+
+
############################
# GetSharedChatById
############################
diff --git a/src/lib/apis/chats/index.ts b/src/lib/apis/chats/index.ts
index b33072e89..bfe7a1638 100644
--- a/src/lib/apis/chats/index.ts
+++ b/src/lib/apis/chats/index.ts
@@ -255,6 +255,55 @@ export const getArchivedChatList = async (
}));
};
+export const getSharedChatList = async (
+ token: string = '',
+ page: number = 1,
+ filter?: object
+) => {
+ let error = null;
+
+ const searchParams = new URLSearchParams();
+ searchParams.append('page', `${page}`);
+
+ if (filter) {
+ Object.entries(filter).forEach(([key, value]) => {
+ if (value !== undefined && value !== null) {
+ searchParams.append(key, value.toString());
+ }
+ });
+ }
+
+ const res = await fetch(`${WEBUI_API_BASE_URL}/chats/shared?${searchParams.toString()}`, {
+ 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.error(err);
+ return null;
+ });
+
+ if (error) {
+ throw error;
+ }
+
+ return res.map((chat) => ({
+ ...chat,
+ time_range: getTimeRange(chat.updated_at)
+ }));
+};
+
export const getAllChats = async (token: string) => {
let error = null;
diff --git a/src/lib/components/chat/Settings/DataControls.svelte b/src/lib/components/chat/Settings/DataControls.svelte
index 748b2548c..5eed8918e 100644
--- a/src/lib/components/chat/Settings/DataControls.svelte
+++ b/src/lib/components/chat/Settings/DataControls.svelte
@@ -24,6 +24,7 @@
import { goto } from '$app/navigation';
import { toast } from 'svelte-sonner';
import ArchivedChatsModal from '$lib/components/layout/ArchivedChatsModal.svelte';
+ import SharedChatsModal from '$lib/components/layout/SharedChatsModal.svelte';
import ConfirmDialog from '$lib/components/common/ConfirmDialog.svelte';
const i18n = getContext('i18n');
@@ -36,6 +37,7 @@
let showArchiveConfirmDialog = false;
let showDeleteConfirmDialog = false;
let showArchivedChatsModal = false;
+ let showSharedChatsModal = false;
let chatImportInputElement: HTMLInputElement;
@@ -136,6 +138,7 @@