diff --git a/backend/open_webui/main.py b/backend/open_webui/main.py index 57660fe9e4..b95166a9fd 100644 --- a/backend/open_webui/main.py +++ b/backend/open_webui/main.py @@ -1760,9 +1760,9 @@ async def chat_completion( "local:" ): # temporary chats are not stored - # Verify chat ownership - chat = Chats.get_chat_by_id_and_user_id(metadata["chat_id"], user.id) - if chat is None and user.role != "admin": # admins can access any chat + # Verify chat ownership — lightweight EXISTS check avoids + # deserializing the full chat JSON blob just to confirm the row exists + if not Chats.is_chat_owner(metadata["chat_id"], user.id) and user.role != "admin": # admins can access any chat raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail=ERROR_MESSAGES.DEFAULT(), diff --git a/backend/open_webui/models/chats.py b/backend/open_webui/models/chats.py index 42c8cb4508..84c67a5b15 100644 --- a/backend/open_webui/models/chats.py +++ b/backend/open_webui/models/chats.py @@ -974,6 +974,41 @@ class ChatTable: except Exception: return None + def is_chat_owner( + self, id: str, user_id: str, db: Optional[Session] = None + ) -> bool: + """ + Lightweight ownership check — uses EXISTS subquery instead of loading + the full Chat row (which includes the potentially large JSON blob). + """ + try: + with get_db_context(db) as db: + return db.query( + exists().where( + and_(Chat.id == id, Chat.user_id == user_id) + ) + ).scalar() + except Exception: + return False + + def get_chat_folder_id( + self, id: str, user_id: str, db: Optional[Session] = None + ) -> Optional[str]: + """ + Fetch only the folder_id column for a chat, without loading the full + JSON blob. Returns None if chat doesn't exist or doesn't belong to user. + """ + try: + with get_db_context(db) as db: + result = ( + db.query(Chat.folder_id) + .filter_by(id=id, user_id=user_id) + .first() + ) + return result[0] if result else None + except Exception: + return None + def get_chats( self, skip: int = 0, limit: int = 50, db: Optional[Session] = None ) -> list[ChatModel]: diff --git a/backend/open_webui/utils/middleware.py b/backend/open_webui/utils/middleware.py index c2b2b07cb1..218deed17e 100644 --- a/backend/open_webui/utils/middleware.py +++ b/backend/open_webui/utils/middleware.py @@ -2078,11 +2078,12 @@ async def process_chat_payload(request, form_data, user, metadata, model): # Folder "Project" handling # Check if the request has chat_id and is inside of a folder + # Uses lightweight column query — only fetches folder_id, not the full chat JSON blob chat_id = metadata.get("chat_id", None) if chat_id and user: - chat = Chats.get_chat_by_id_and_user_id(chat_id, user.id) - if chat and chat.folder_id: - folder = Folders.get_folder_by_id_and_user_id(chat.folder_id, user.id) + folder_id = Chats.get_chat_folder_id(chat_id, user.id) + if folder_id: + folder = Folders.get_folder_by_id_and_user_id(folder_id, user.id) if folder and folder.data: if "system_prompt" in folder.data: