From 837d8b53ef688137504b3d970aefa2b469347445 Mon Sep 17 00:00:00 2001 From: Classic298 <27028174+Classic298@users.noreply.github.com> Date: Thu, 20 Aug 2026 22:06:41 +0200 Subject: [PATCH] refactor: remove unused database table methods Fifty methods across the database table classes have no call site: no router, no util, no socket handler and no other model reaches them. They cover reads, deletes and access control writes that were either replaced by a different query or never wired up. Two access control converters and two response models were reachable only from those methods, so they go in the same pass rather than being left for the next one. One of the two response models was a shadow copy of a live model with the same name in another module, which is a trap worth closing. These are internal data access helpers, so no HTTP or socket surface changes. This removes around 800 lines of query code that still has to be read and kept correct every time those files are touched, and anything genuinely needed later is cheaper to write against the schema as it stands then than to carry as code nothing exercises. --- backend/open_webui/models/access_grants.py | 232 -------------------- backend/open_webui/models/auths.py | 12 - backend/open_webui/models/calendar.py | 15 -- backend/open_webui/models/channels.py | 72 ------ backend/open_webui/models/chat_messages.py | 21 -- backend/open_webui/models/chats.py | 67 +----- backend/open_webui/models/feedbacks.py | 20 -- backend/open_webui/models/files.py | 46 ---- backend/open_webui/models/folders.py | 17 -- backend/open_webui/models/functions.py | 10 - backend/open_webui/models/groups.py | 98 --------- backend/open_webui/models/memories.py | 19 -- backend/open_webui/models/messages.py | 17 -- backend/open_webui/models/notes.py | 13 -- backend/open_webui/models/oauth_sessions.py | 22 -- backend/open_webui/models/prompt_history.py | 14 +- backend/open_webui/models/prompts.py | 85 ------- backend/open_webui/models/shared_chats.py | 11 - backend/open_webui/models/skills.py | 9 - backend/open_webui/models/tags.py | 18 -- backend/open_webui/models/users.py | 9 - 21 files changed, 2 insertions(+), 825 deletions(-) diff --git a/backend/open_webui/models/access_grants.py b/backend/open_webui/models/access_grants.py index 03a7ef7202..10c8fbfa3e 100644 --- a/backend/open_webui/models/access_grants.py +++ b/backend/open_webui/models/access_grants.py @@ -80,73 +80,6 @@ class AccessGrantResponse(BaseModel): #################### -def access_control_to_grants( - resource_type: str, - resource_id: str, - access_control: Optional[dict], -) -> list[dict]: - """ - Convert an old-style access_control JSON dict to a flat list of grant dicts. - - Semantics: - - None → public read (user:* read) — except files which are private - - {} → private/owner-only (no grants) - - {read: {group_ids, user_ids}, write: {group_ids, user_ids}} → specific grants - - Returns a list of dicts with keys: resource_type, resource_id, principal_type, principal_id, permission - """ - grants = [] - - if access_control is None: - # NULL → public read (user:* for read) - # Exception: files with NULL are private (owner-only), no grants needed - if resource_type != 'file': - grants.append( - { - 'resource_type': resource_type, - 'resource_id': resource_id, - 'principal_type': 'user', - 'principal_id': '*', - 'permission': 'read', - } - ) - return grants - - # {} → private/owner-only, no grants - if not access_control: - return grants - - # Parse structured permissions - for permission in ['read', 'write']: - perm_data = access_control.get(permission, {}) - if not perm_data: - continue - - for group_id in perm_data.get('group_ids', []): - grants.append( - { - 'resource_type': resource_type, - 'resource_id': resource_id, - 'principal_type': 'group', - 'principal_id': group_id, - 'permission': permission, - } - ) - - for user_id in perm_data.get('user_ids', []): - grants.append( - { - 'resource_type': resource_type, - 'resource_id': resource_id, - 'principal_type': 'user', - 'principal_id': user_id, - 'permission': permission, - } - ) - - return grants - - def normalize_access_grants(access_grants: Optional[list]) -> list[dict]: """ Normalize direct access_grants payloads from API forms. @@ -273,118 +206,12 @@ def strip_anyone_access_grants(access_grants: Optional[list]) -> list: ] -def grants_to_access_control(grants: list) -> Optional[dict]: - """ - Convert a list of grant objects (AccessGrantModel or AccessGrantResponse) - back to the old-style access_control JSON dict for backward compatibility. - - Semantics: - - [] (empty) → {} (private/owner-only) - - Contains user:*:read → None (public), but write grants are preserved - - Otherwise → {read: {group_ids, user_ids}, write: {group_ids, user_ids}} - - Note: "public" (user:*:read) still allows additional write permissions - to coexist. When the wildcard read is present the function returns None - for the legacy dict, so callers that need write info should inspect the - grants list directly. - """ - if not grants: - return {} # No grants = private/owner-only - - result = { - 'read': {'group_ids': [], 'user_ids': []}, - 'write': {'group_ids': [], 'user_ids': []}, - } - - is_public = False - for grant in grants: - if grant.principal_type == 'user' and grant.principal_id == '*' and grant.permission == 'read': - is_public = True - continue # Don't add wildcard to user_ids list - - if grant.permission not in ('read', 'write'): - continue - - if grant.principal_type == 'group': - if grant.principal_id not in result[grant.permission]['group_ids']: - result[grant.permission]['group_ids'].append(grant.principal_id) - elif grant.principal_type == 'user': - if grant.principal_id not in result[grant.permission]['user_ids']: - result[grant.permission]['user_ids'].append(grant.principal_id) - - if is_public: - return None # Public read access - - return result - - #################### # Table Operations #################### class AccessGrantsTable: - async def grant_access( - self, - resource_type: str, - resource_id: str, - principal_type: str, - principal_id: str, - permission: str, - db: Optional[AsyncSession] = None, - ) -> Optional[AccessGrantModel]: - """Add a single access grant. Idempotent (ignores duplicates).""" - async with get_async_db_context(db) as db: - # Check for existing grant - result = await db.execute( - select(AccessGrant).filter_by( - resource_type=resource_type, - resource_id=resource_id, - principal_type=principal_type, - principal_id=principal_id, - permission=permission, - ) - ) - existing = result.scalars().first() - if existing: - return AccessGrantModel.model_validate(existing) - - grant = AccessGrant( - id=str(uuid.uuid4()), - resource_type=resource_type, - resource_id=resource_id, - principal_type=principal_type, - principal_id=principal_id, - permission=permission, - created_at=int(time.time()), - ) - db.add(grant) - await db.commit() - return AccessGrantModel.model_validate(grant) - - async def revoke_access( - self, - resource_type: str, - resource_id: str, - principal_type: str, - principal_id: str, - permission: str, - db: Optional[AsyncSession] = None, - ) -> bool: - """Remove a single access grant.""" - async with get_async_db_context(db) as db: - result = await db.execute( - delete(AccessGrant).filter_by( - resource_type=resource_type, - resource_id=resource_id, - principal_type=principal_type, - principal_id=principal_id, - permission=permission, - ) - ) - await db.commit() - return result.rowcount > 0 - async def revoke_all_access( self, resource_type: str, @@ -402,44 +229,6 @@ class AccessGrantsTable: await db.commit() return result.rowcount - async def set_access_control( - self, - resource_type: str, - resource_id: str, - access_control: Optional[dict], - db: Optional[AsyncSession] = None, - ) -> list[AccessGrantModel]: - """ - Replace all grants for a resource from an access_control JSON dict. - This is the primary bridge for backward compat with the frontend. - """ - async with get_async_db_context(db) as db: - # Delete all existing grants for this resource - await db.execute( - delete(AccessGrant).filter_by( - resource_type=resource_type, - resource_id=resource_id, - ) - ) - - # Convert JSON to grant dicts - grant_dicts = access_control_to_grants(resource_type, resource_id, access_control) - - # Insert new grants - results = [] - for grant_dict in grant_dicts: - grant = AccessGrant( - id=str(uuid.uuid4()), - **grant_dict, - created_at=int(time.time()), - ) - db.add(grant) - results.append(grant) - - await db.commit() - - return [AccessGrantModel.model_validate(g) for g in results] - async def set_access_grants( self, resource_type: str, @@ -477,27 +266,6 @@ class AccessGrantsTable: await db.commit() return [AccessGrantModel.model_validate(g) for g in results] - async def get_access_control( - self, - resource_type: str, - resource_id: str, - db: Optional[AsyncSession] = None, - ) -> Optional[dict]: - """ - Reconstruct the old-style access_control JSON dict from grants. - For backward compat with the frontend. - """ - async with get_async_db_context(db) as db: - result = await db.execute( - select(AccessGrant).filter_by( - resource_type=resource_type, - resource_id=resource_id, - ) - ) - grants = result.scalars().all() - grant_models = [AccessGrantModel.model_validate(g) for g in grants] - return grants_to_access_control(grant_models) - async def get_grants_by_resource( self, resource_type: str, diff --git a/backend/open_webui/models/auths.py b/backend/open_webui/models/auths.py index 629c5eb6c5..80df2e4a4a 100644 --- a/backend/open_webui/models/auths.py +++ b/backend/open_webui/models/auths.py @@ -163,18 +163,6 @@ class AuthsTable: return return resolved - async def authenticate_user_by_api_key( - self, - api_key: str, - db: AsyncSession | None = None, - ) -> UserModel | None: - """Look up the user that owns the given API key.""" - log.info('authenticate_user_by_api_key') - if not api_key: - return - # delegate to the Users model for the actual lookup - return await Users.get_user_by_api_key(api_key, db=db) - async def authenticate_user_by_email( self, email: str, diff --git a/backend/open_webui/models/calendar.py b/backend/open_webui/models/calendar.py index 52470f1178..56979cad34 100644 --- a/backend/open_webui/models/calendar.py +++ b/backend/open_webui/models/calendar.py @@ -822,21 +822,6 @@ class CalendarEventAttendeeTable: await db.commit() return CalendarEventAttendeeModel.model_validate(att) - async def get_attendees_by_event( - self, event_id: str, db: Optional[AsyncSession] = None - ) -> list[CalendarEventAttendeeModel]: - async with get_async_db_context(db) as db: - result = await db.execute(select(CalendarEventAttendee).filter(CalendarEventAttendee.event_id == event_id)) - return [CalendarEventAttendeeModel.model_validate(r) for r in result.scalars().all()] - - async def get_events_by_attendee(self, user_id: str, db: Optional[AsyncSession] = None) -> list[str]: - """Return event IDs where user is an attendee.""" - async with get_async_db_context(db) as db: - result = await db.execute( - select(CalendarEventAttendee.event_id).filter(CalendarEventAttendee.user_id == user_id) - ) - return [r[0] for r in result.all()] - Calendars = CalendarTable() CalendarEvents = CalendarEventTable() diff --git a/backend/open_webui/models/channels.py b/backend/open_webui/models/channels.py index c30e2347b9..d2235390c9 100644 --- a/backend/open_webui/models/channels.py +++ b/backend/open_webui/models/channels.py @@ -563,26 +563,6 @@ class ChannelTable: await db.commit() return channel_member - async def leave_channel(self, channel_id: str, user_id: str, db: Optional[AsyncSession] = None) -> bool: - async with get_async_db_context(db) as db: - result = await db.execute( - select(ChannelMember).filter( - ChannelMember.channel_id == channel_id, - ChannelMember.user_id == user_id, - ) - ) - membership = result.scalars().first() - if not membership: - return False - - membership.status = 'left' - membership.is_active = False - membership.left_at = int(time.time_ns()) - membership.updated_at = int(time.time_ns()) - - await db.commit() - return True - async def get_member_by_channel_and_user_id( self, channel_id: str, user_id: str, db: Optional[AsyncSession] = None ) -> Optional[ChannelMemberModel]: @@ -604,30 +584,6 @@ class ChannelTable: memberships = result.scalars().all() return [ChannelMemberModel.model_validate(membership) for membership in memberships] - async def pin_channel( - self, - channel_id: str, - user_id: str, - is_pinned: bool, - db: Optional[AsyncSession] = None, - ) -> bool: - async with get_async_db_context(db) as db: - result = await db.execute( - select(ChannelMember).filter( - ChannelMember.channel_id == channel_id, - ChannelMember.user_id == user_id, - ) - ) - membership = result.scalars().first() - if not membership: - return False - - membership.is_channel_pinned = is_pinned - membership.updated_at = int(time.time_ns()) - - await db.commit() - return True - async def update_member_last_read_at( self, channel_id: str, user_id: str, db: Optional[AsyncSession] = None ) -> bool: @@ -695,23 +651,6 @@ class ChannelTable: except Exception: return None - async def get_channels_by_file_id(self, file_id: str, db: Optional[AsyncSession] = None) -> list[ChannelModel]: - async with get_async_db_context(db) as db: - result = await db.execute(select(ChannelFile).filter(ChannelFile.file_id == file_id)) - channel_files = result.scalars().all() - channel_ids = [cf.channel_id for cf in channel_files] - result = await db.execute(select(Channel).filter(Channel.id.in_(channel_ids))) - channels = result.scalars().all() - grants_map = await AccessGrants.get_grants_by_resources('channel', channel_ids, db=db) - return [ - await self._to_channel_model( - channel, - access_grants=grants_map.get(channel.id, []), - db=db, - ) - for channel in channels - ] - async def get_channels_by_file_id_and_user_id( self, file_id: str, user_id: str, db: Optional[AsyncSession] = None ) -> list[ChannelModel]: @@ -898,17 +837,6 @@ class ChannelTable: except Exception: return False - async def remove_file_from_channel_by_id( - self, channel_id: str, file_id: str, db: Optional[AsyncSession] = None - ) -> bool: - try: - async with get_async_db_context(db) as db: - await db.execute(delete(ChannelFile).filter_by(channel_id=channel_id, file_id=file_id)) - await db.commit() - return True - except Exception: - return False - async def delete_channel_by_id(self, id: str, db: Optional[AsyncSession] = None) -> bool: async with get_async_db_context(db) as db: await AccessGrants.revoke_all_access('channel', id, db=db) diff --git a/backend/open_webui/models/chat_messages.py b/backend/open_webui/models/chat_messages.py index 4977df70c3..0e6c0fb691 100644 --- a/backend/open_webui/models/chat_messages.py +++ b/backend/open_webui/models/chat_messages.py @@ -491,12 +491,6 @@ class ChatMessageTable: chat_ids = result.all() return [chat_id for chat_id, _ in chat_ids] - async def delete_messages_by_chat_id(self, chat_id: str, db: Optional[AsyncSession] = None) -> bool: - async with get_async_db_context(db) as db: - await db.execute(delete(ChatMessage).filter_by(chat_id=chat_id)) - await db.commit() - return True - async def delete_message_ids_by_chat_id( self, chat_id: str, @@ -749,21 +743,6 @@ class ChatMessageTable: 'active_days': len(active_days), } - async def get_user_first_message_created_at( - self, - user_id: str, - db: Optional[AsyncSession] = None, - ) -> Optional[int]: - async with get_async_db_context(db) as db: - result = await db.execute( - select(func.min(ChatMessage.created_at)).filter( - ChatMessage.user_id == user_id, - ChatMessage.created_at.isnot(None), - ) - ) - value = result.scalar() - return int(value) if value else None - async def get_user_daily_usage( self, user_id: str, diff --git a/backend/open_webui/models/chats.py b/backend/open_webui/models/chats.py index 9854af6d3a..e9947c7c70 100644 --- a/backend/open_webui/models/chats.py +++ b/backend/open_webui/models/chats.py @@ -12,7 +12,7 @@ from open_webui.internal.db import Base, JSONField, get_async_db_context from open_webui.models.automations import AutomationRun from open_webui.models.chat_messages import ChatMessage, ChatMessages from open_webui.models.folders import Folders -from open_webui.models.tags import Tag, TagModel, Tags +from open_webui.models.tags import Tag, Tags from open_webui.utils.misc import get_output_text, sanitize_data_for_db, sanitize_text_for_db from pydantic import BaseModel, ConfigDict, field_validator from sqlalchemy import ( @@ -289,14 +289,6 @@ class ChatTitleIdResponse(BaseModel): active: bool = False -class SharedChatResponse(BaseModel): - id: str - title: str - share_id: str | None = None - updated_at: int - created_at: int - - class ChatListResponse(BaseModel): items: list[ChatModel] total: int @@ -1356,19 +1348,6 @@ class ChatTable: result = await session.execute(stmt.where(Chat.meta['internal'].as_boolean().is_not(True))) return result.scalar() or 0 - async def get_shared_chat_list_by_user_id( - self, - user_id: str, - filter: dict | None = None, - skip: int = 0, - limit: int = 50, - db: AsyncSession | None = None, - ) -> list[SharedChatResponse]: - """Delegate to SharedChats for listing shared chats by user.""" - from open_webui.models.shared_chats import SharedChats - - return await SharedChats.get_by_user_id(user_id, filter=filter, skip=skip, limit=limit, db=db) - async def get_chat_list_by_user_id( self, user_id: str, @@ -1474,20 +1453,6 @@ class ChatTable: for chat in all_chats ] - async def get_chat_list_by_chat_ids( - self, - chat_ids: list[str], - skip: int = 0, - limit: int = 50, - db: AsyncSession | None = None, - ) -> list[ChatModel]: - async with get_async_db_context(db) as session: - stmt = select(Chat).filter(Chat.id.in_(chat_ids)).filter_by(archived=False) - stmt = stmt.where(Chat.meta['internal'].as_boolean().is_not(True)) - result = await session.execute(stmt.order_by(Chat.updated_at.desc())) - all_chats = result.scalars().all() - return [ChatModel.model_validate(chat) for chat in all_chats] - async def get_chat_metas_by_chat_ids( self, chat_ids: list[str], @@ -2134,16 +2099,6 @@ class ChatTable: except Exception: return None - async def get_chat_tags_by_id_and_user_id( - self, id: str, user_id: str, db: AsyncSession | None = None - ) -> list[TagModel]: - async with get_async_db_context(db) as session: - stmt = select(Chat.meta).where(Chat.id == id) - result = await session.execute(stmt) - meta = result.scalar_one_or_none() - tag_ids = (meta or {}).get('tags', []) - return await Tags.get_tags_by_ids_and_user_id(tag_ids, user_id, db=session) - async def get_chat_list_by_user_id_and_tag_name( self, user_id: str, @@ -2280,17 +2235,6 @@ class ChatTable: orphans = [tag_id for tag_id in tag_ids if counts.get(tag_id, 0) <= threshold] await Tags.delete_tags_by_ids_and_user_id(orphans, user_id, db=session) - async def count_chats_by_folder_id_and_user_id( - self, folder_id: str, user_id: str, db: AsyncSession | None = None - ) -> int: - async with get_async_db_context(db) as session: - stmt = select(func.count(Chat.id)).filter_by(user_id=user_id, folder_id=folder_id) - result = await session.execute(stmt.where(Chat.meta['internal'].as_boolean().is_not(True))) - count = result.scalar() - - log.info("Count of chats for folder '%s': %s", folder_id, count) - return count - async def count_chats_by_folder_ids_and_user_id( self, folder_ids: list[str], user_id: str, db: AsyncSession | None = None ) -> int: @@ -2498,15 +2442,6 @@ class ChatTable: all_chat_files = result.scalars().all() return [ChatFileModel.model_validate(chat_file) for chat_file in all_chat_files] - async def delete_chat_file(self, chat_id: str, file_id: str, db: AsyncSession | None = None) -> bool: - try: - async with get_async_db_context(db) as session: - await session.execute(delete(ChatFile).filter_by(chat_id=chat_id, file_id=file_id)) - await session.commit() - return True - except Exception: - return False - async def get_shared_chat_ids_by_file_id(self, file_id: str, db: AsyncSession | None = None) -> list[str]: """Return IDs of chats that contain this file and have an active share link.""" async with get_async_db_context(db) as session: diff --git a/backend/open_webui/models/feedbacks.py b/backend/open_webui/models/feedbacks.py index ca1fe39b38..e21bb0c3d4 100644 --- a/backend/open_webui/models/feedbacks.py +++ b/backend/open_webui/models/feedbacks.py @@ -197,21 +197,6 @@ class FeedbackTable: except Exception: return None - async def get_feedbacks_by_chat_id(self, chat_id: str, db: Optional[AsyncSession] = None) -> list[FeedbackModel]: - """Get all feedbacks for a specific chat.""" - try: - async with get_async_db_context(db) as db: - # meta.chat_id stores the chat reference - result = await db.execute( - select(Feedback) - .filter(Feedback.meta['chat_id'].as_string() == chat_id) - .order_by(Feedback.created_at.desc()) - ) - feedbacks = result.scalars().all() - return [FeedbackModel.model_validate(fb) for fb in feedbacks] - except Exception: - return [] - async def get_feedback_items( self, filter: dict = {}, @@ -422,11 +407,6 @@ class FeedbackTable: for date_str, counts in sorted(daily_counts.items()) ] - async def get_feedbacks_by_type(self, type: str, db: Optional[AsyncSession] = None) -> list[FeedbackModel]: - async with get_async_db_context(db) as db: - result = await db.execute(select(Feedback).filter_by(type=type).order_by(Feedback.updated_at.desc())) - return [FeedbackModel.model_validate(feedback) for feedback in result.scalars().all()] - async def get_feedbacks_by_user_id( self, user_id: str, diff --git a/backend/open_webui/models/files.py b/backend/open_webui/models/files.py index d1f61c7cc7..c39d2985fd 100644 --- a/backend/open_webui/models/files.py +++ b/backend/open_webui/models/files.py @@ -179,27 +179,6 @@ class FilesTable: except Exception: return None - async def get_file_metadata_by_id(self, id: str, db: AsyncSession | None = None) -> FileMetadataResponse | None: - async with get_async_db_context(db) as db: - try: - file = await db.get(File, id) - if not file: - return None - return FileMetadataResponse( - id=file.id, - hash=file.hash, - meta=file.meta, - created_at=file.created_at, - updated_at=file.updated_at, - ) - except Exception: - return None - - async def get_files(self, db: AsyncSession | None = None) -> list[FileModel]: - async with get_async_db_context(db) as db: - result = await db.execute(select(File)) - return [FileModel.model_validate(file) for file in result.scalars().all()] - async def count_files_by_user_id( self, user_id: str | None = None, @@ -226,31 +205,6 @@ class FilesTable: result = await db.execute(select(File).filter(File.id.in_(ids)).order_by(File.updated_at.desc())) return [FileModel.model_validate(file) for file in result.scalars().all()] - async def get_file_metadatas_by_ids( - self, ids: list[str], db: AsyncSession | None = None - ) -> list[FileMetadataResponse]: - async with get_async_db_context(db) as db: - result = await db.execute( - select(File.id, File.hash, File.meta, File.created_at, File.updated_at) - .filter(File.id.in_(ids)) - .order_by(File.updated_at.desc()) - ) - return [ - FileMetadataResponse( - id=row.id, - hash=row.hash, - meta=row.meta, - created_at=row.created_at, - updated_at=row.updated_at, - ) - for row in result.all() - ] - - async def get_files_by_user_id(self, user_id: str, db: AsyncSession | None = None) -> list[FileModel]: - async with get_async_db_context(db) as db: - result = await db.execute(select(File).filter_by(user_id=user_id)) - return [FileModel.model_validate(file) for file in result.scalars().all()] - async def get_file_list( self, user_id: str | None = None, diff --git a/backend/open_webui/models/folders.py b/backend/open_webui/models/folders.py index 24bb5e5af0..e68a77d5ab 100644 --- a/backend/open_webui/models/folders.py +++ b/backend/open_webui/models/folders.py @@ -442,22 +442,5 @@ class FolderTable: results = list(results.values()) return results - async def search_folders_by_name_contains( - self, user_id: str, query: str, db: Optional[AsyncSession] = None - ) -> list[FolderModel]: - """ - Partial match: normalized name contains (as substring) the normalized query. - """ - normalized_query = self.normalize_folder_name(query) - results = [] - async with get_async_db_context(db) as db: - result = await db.execute(select(Folder).filter_by(user_id=user_id)) - folders = result.scalars().all() - for folder in folders: - norm_name = self.normalize_folder_name(folder.name) - if normalized_query in norm_name: - results.append(FolderModel.model_validate(folder)) - return results - Folders = FolderTable() diff --git a/backend/open_webui/models/functions.py b/backend/open_webui/models/functions.py index 573880d521..2fa0d19053 100644 --- a/backend/open_webui/models/functions.py +++ b/backend/open_webui/models/functions.py @@ -269,11 +269,6 @@ class FunctionsTable: result = await db.execute(select(Function).filter_by(type=type)) return [FunctionModel.model_validate(function) for function in result.scalars().all()] - async def get_global_filter_functions(self, db: AsyncSession | None = None) -> list[FunctionModel]: - async with get_async_db_context(db) as db: - result = await db.execute(select(Function).filter_by(type='filter', is_active=True, is_global=True)) - return [FunctionModel.model_validate(function) for function in result.scalars().all()] - async def get_active_function_ids_by_type( self, type: str, db: AsyncSession | None = None ) -> list[tuple[str, bool]]: @@ -286,11 +281,6 @@ class FunctionsTable: """Return (id, is_global) for active filters without fetching plugin source.""" return await self.get_active_function_ids_by_type('filter', db=db) - async def get_global_action_functions(self, db: AsyncSession | None = None) -> list[FunctionModel]: - async with get_async_db_context(db) as db: - result = await db.execute(select(Function).filter_by(type='action', is_active=True, is_global=True)) - return [FunctionModel.model_validate(function) for function in result.scalars().all()] - async def get_function_valves_by_id(self, id: str, db: AsyncSession | None = None) -> dict | None: async with get_async_db_context(db) as db: try: diff --git a/backend/open_webui/models/groups.py b/backend/open_webui/models/groups.py index 611afe576d..85e89a5c25 100644 --- a/backend/open_webui/models/groups.py +++ b/backend/open_webui/models/groups.py @@ -128,11 +128,6 @@ class GroupUpdateForm(GroupForm): pass -class GroupListResponse(BaseModel): - items: list[GroupResponse] = [] - total: int = 0 - - class GroupTable: def _ensure_default_share_config(self, group_data: dict) -> dict: """Ensure the group data dict has a default share config if not already set.""" @@ -247,61 +242,6 @@ class GroupTable: for group, count in rows ] - async def search_groups( - self, - filter: Optional[dict] = None, - skip: int = 0, - limit: int = 30, - db: Optional[AsyncSession] = None, - ) -> GroupListResponse: - async with get_async_db_context(db) as db: - stmt = select(Group) - - if filter: - if 'query' in filter: - stmt = stmt.filter(Group.name.ilike(f'%{filter["query"]}%')) - if 'member_id' in filter: - stmt = stmt.filter( - Group.id.in_(select(GroupMember.group_id).where(GroupMember.user_id == filter['member_id'])) - ) - - if 'share' in filter: - share_value = filter['share'] - stmt = stmt.filter(Group.data.op('->>')('share') == str(share_value)) - - # Get total count - count_result = await db.execute(select(func.count()).select_from(stmt.subquery())) - total = count_result.scalar() - - member_count = ( - select(func.count(GroupMember.user_id)) - .where(GroupMember.group_id == Group.id) - .correlate(Group) - .scalar_subquery() - .label('member_count') - ) - result = await db.execute( - select(Group, member_count) - .where(Group.id.in_(select(stmt.subquery().c.id))) - .order_by(Group.updated_at.desc()) - .offset(skip) - .limit(limit) - ) - rows = result.all() - - return { - 'items': [ - GroupResponse.model_validate( - { - **GroupModel.model_validate(group).model_dump(), - 'member_count': count or 0, - } - ) - for group, count in rows - ], - 'total': total, - } - async def get_groups_by_member_id(self, user_id: str, db: Optional[AsyncSession] = None) -> list[GroupModel]: async with get_async_db_context(db) as db: result = await db.execute( @@ -352,22 +292,6 @@ class GroupTable: return [m[0] for m in members] - async def get_group_user_ids_by_ids( - self, group_ids: list[str], db: Optional[AsyncSession] = None - ) -> dict[str, list[str]]: - async with get_async_db_context(db) as db: - result = await db.execute( - select(GroupMember.group_id, GroupMember.user_id).filter(GroupMember.group_id.in_(group_ids)) - ) - members = result.all() - - group_user_ids: dict[str, list[str]] = {group_id: [] for group_id in group_ids} - - for group_id, user_id in members: - group_user_ids[group_id].append(user_id) - - return group_user_ids - async def set_group_user_ids_by_id( self, group_id: str, user_ids: list[str], db: Optional[AsyncSession] = None ) -> None: @@ -397,18 +321,6 @@ class GroupTable: count = result.scalar() return count if count else 0 - async def get_group_member_counts_by_ids(self, ids: list[str], db: Optional[AsyncSession] = None) -> dict[str, int]: - if not ids: - return {} - async with get_async_db_context(db) as db: - result = await db.execute( - select(GroupMember.group_id, func.count(GroupMember.user_id)) - .filter(GroupMember.group_id.in_(ids)) - .group_by(GroupMember.group_id) - ) - rows = result.all() - return {group_id: count for group_id, count in rows} - async def update_group_by_id( self, id: str, @@ -441,16 +353,6 @@ class GroupTable: except Exception: return False - async def delete_all_groups(self, db: Optional[AsyncSession] = None) -> bool: - async with get_async_db_context(db) as db: - try: - await db.execute(delete(Group)) - await db.commit() - - return True - except Exception: - return False - async def remove_user_from_all_groups(self, user_id: str, db: Optional[AsyncSession] = None) -> bool: async with get_async_db_context(db) as db: try: diff --git a/backend/open_webui/models/memories.py b/backend/open_webui/models/memories.py index e04f16b962..1baea78ea6 100644 --- a/backend/open_webui/models/memories.py +++ b/backend/open_webui/models/memories.py @@ -123,25 +123,6 @@ class MemoriesTable: except Exception: return None - async def get_memory_by_id(self, id: str, db: AsyncSession | None = None) -> MemoryModel | None: - async with get_async_db_context(db) as db: - try: - memory = await db.get(Memory, id) - return MemoryModel.model_validate(memory) if memory else None - except Exception: - return None - - async def delete_memory_by_id(self, id: str, db: AsyncSession | None = None) -> bool: - async with get_async_db_context(db) as db: - try: - await db.execute(delete(Memory).filter_by(id=id)) - await db.commit() - - return True - - except Exception: - return False - async def delete_memories_by_user_id(self, user_id: str, db: AsyncSession | None = None) -> bool: async with get_async_db_context(db) as db: try: diff --git a/backend/open_webui/models/messages.py b/backend/open_webui/models/messages.py index 82ac3336dd..bded4ebffd 100644 --- a/backend/open_webui/models/messages.py +++ b/backend/open_webui/models/messages.py @@ -273,11 +273,6 @@ class MessageTable: ) return messages - async def get_reply_user_ids_by_message_id(self, id: str, db: Optional[AsyncSession] = None) -> list[str]: - async with get_async_db_context(db) as db: - result = await db.execute(select(Message.user_id).filter_by(parent_id=id)) - return [row[0] for row in result.all()] - async def get_messages_by_channel_id( self, channel_id: str, @@ -573,18 +568,6 @@ class MessageTable: await db.commit() return True - async def delete_reactions_by_id(self, id: str, db: Optional[AsyncSession] = None) -> bool: - async with get_async_db_context(db) as db: - await db.execute(delete(MessageReaction).filter_by(message_id=id)) - await db.commit() - return True - - async def delete_replies_by_id(self, id: str, db: Optional[AsyncSession] = None) -> bool: - async with get_async_db_context(db) as db: - await db.execute(delete(Message).filter_by(parent_id=id)) - await db.commit() - return True - async def delete_message_by_id(self, id: str, db: Optional[AsyncSession] = None) -> bool: async with get_async_db_context(db) as db: await db.execute(delete(Message).filter_by(id=id)) diff --git a/backend/open_webui/models/notes.py b/backend/open_webui/models/notes.py index e9c06021e1..c967480263 100644 --- a/backend/open_webui/models/notes.py +++ b/backend/open_webui/models/notes.py @@ -186,19 +186,6 @@ class NoteTable: await AccessGrants.set_access_grants('note', note.id, form_data.access_grants, db=db) return await self._to_note_model(new_note, db=db) - async def get_notes(self, skip: int = 0, limit: int = 50, db: Optional[AsyncSession] = None) -> list[NoteModel]: - async with get_async_db_context(db) as db: - stmt = select(Note).order_by(Note.updated_at.desc()) - if skip is not None: - stmt = stmt.offset(skip) - if limit is not None: - stmt = stmt.limit(limit) - result = await db.execute(stmt) - notes = result.scalars().all() - note_ids = [note.id for note in notes] - grants_map = await AccessGrants.get_grants_by_resources('note', note_ids, db=db) - return [await self._to_note_model(note, access_grants=grants_map.get(note.id, []), db=db) for note in notes] - async def search_notes( self, user_id: str, diff --git a/backend/open_webui/models/oauth_sessions.py b/backend/open_webui/models/oauth_sessions.py index 25a50ced55..d0ef4194f9 100644 --- a/backend/open_webui/models/oauth_sessions.py +++ b/backend/open_webui/models/oauth_sessions.py @@ -305,17 +305,6 @@ class OAuthSessionTable: log.error(f'Error deleting OAuth session: {e}') return False - async def delete_sessions_by_user_id(self, user_id: str, db: Optional[AsyncSession] = None) -> bool: - """Delete all OAuth sessions for a user""" - try: - async with get_async_db_context(db) as db: - await db.execute(delete(OAuthSession).filter_by(user_id=user_id)) - await db.commit() - return True - except Exception as e: - log.error(f'Error deleting OAuth sessions by user ID: {e}') - return False - async def delete_sessions_by_user_id_and_provider( self, user_id: str, provider: str, db: Optional[AsyncSession] = None ) -> bool: @@ -329,16 +318,5 @@ class OAuthSessionTable: log.error(f'Error deleting OAuth sessions for user {user_id} and provider {provider}: {e}') return False - async def delete_sessions_by_provider(self, provider: str, db: Optional[AsyncSession] = None) -> bool: - """Delete all OAuth sessions for a provider""" - try: - async with get_async_db_context(db) as db: - await db.execute(delete(OAuthSession).filter_by(provider=provider)) - await db.commit() - return True - except Exception as e: - log.error(f'Error deleting OAuth sessions by provider {provider}: {e}') - return False - OAuthSessions = OAuthSessionTable() diff --git a/backend/open_webui/models/prompt_history.py b/backend/open_webui/models/prompt_history.py index 074bb5d039..063bc5e196 100644 --- a/backend/open_webui/models/prompt_history.py +++ b/backend/open_webui/models/prompt_history.py @@ -8,7 +8,7 @@ from typing import Optional from open_webui.internal.db import Base, get_async_db_context from open_webui.models.users import UserResponse, Users from pydantic import BaseModel, ConfigDict -from sqlalchemy import JSON, BigInteger, Column, Index, Text, delete, func, select +from sqlalchemy import JSON, BigInteger, Column, Index, Text, delete, select from sqlalchemy.ext.asyncio import AsyncSession #################### @@ -133,18 +133,6 @@ class PromptHistoryTable: return PromptHistoryModel.model_validate(entry) return None - async def get_history_count( - self, - prompt_id: str, - db: Optional[AsyncSession] = None, - ) -> int: - """Get the number of history entries for a prompt.""" - async with get_async_db_context(db) as db: - result = await db.execute( - select(func.count()).select_from(PromptHistory).filter(PromptHistory.prompt_id == prompt_id) - ) - return result.scalar() - async def compute_diff( self, from_id: str, diff --git a/backend/open_webui/models/prompts.py b/backend/open_webui/models/prompts.py index 54e67ac2f4..1694d80171 100644 --- a/backend/open_webui/models/prompts.py +++ b/backend/open_webui/models/prompts.py @@ -409,74 +409,6 @@ class PromptsTable: return PromptListResponse(items=prompts, total=total) - async def update_prompt_by_command( - self, - command: str, - form_data: PromptForm, - user_id: str, - db: AsyncSession | None = None, - ) -> PromptModel | None: - if not command: - return None - try: # database transaction - async with get_async_db_context(db) as session: - result = await session.execute(select(Prompt).filter_by(command=command)) - prompt = result.scalars().first() - if not prompt: - return None - - latest_history = await PromptHistories.get_latest_history_entry(prompt.id, db=session) - parent_id = latest_history.id if latest_history else None - current_access_grants = await self._get_access_grants(prompt.id, db=session) - - # Check if content changed to decide on history creation - content_changed = ( - prompt.name != form_data.name - or prompt.content != form_data.content - or form_data.access_grants is not None - ) - - # Update prompt fields - prompt.name = form_data.name - prompt.content = form_data.content - prompt.data = form_data.data or prompt.data - prompt.meta = form_data.meta or prompt.meta - prompt.updated_at = int(time.time()) - if form_data.access_grants is not None: - await AccessGrants.set_access_grants('prompt', prompt.id, form_data.access_grants, db=session) - current_access_grants = await self._get_access_grants(prompt.id, db=session) - - await session.commit() - - # Create history entry only if content changed - if content_changed: - snapshot = { - 'name': form_data.name, - 'content': form_data.content, - 'command': command, - 'data': form_data.data or {}, - 'meta': form_data.meta or {}, - 'access_grants': [grant.model_dump() for grant in current_access_grants], - } - - history_entry = await PromptHistories.create_history_entry( - prompt_id=prompt.id, - snapshot=snapshot, - user_id=user_id, - parent_id=parent_id, - commit_message=form_data.commit_message, - db=db, - ) - - # Set as production if flag is True (default) - if form_data.is_production and history_entry: - prompt.version_id = history_entry.id - await session.commit() - - return await self._to_prompt_model(prompt, db=session) - except Exception: - return None - async def update_prompt_by_id( self, prompt_id: str, @@ -641,23 +573,6 @@ class PromptsTable: except Exception: return None - async def delete_prompt_by_command(self, command: str, db: AsyncSession | None = None) -> bool: - """Permanently delete a prompt and its history.""" - try: - async with get_async_db_context(db) as session: - result = await session.execute(select(Prompt).filter_by(command=command)) - prompt = result.scalars().first() - if prompt: - await PromptHistories.delete_history_by_prompt_id(prompt.id, db=session) - await AccessGrants.revoke_all_access('prompt', prompt.id, db=session) - - await session.delete(prompt) - await session.commit() - return True - return False - except Exception: - return False - async def delete_prompt_by_id(self, prompt_id: str, db: AsyncSession | None = None) -> bool: """Permanently delete a prompt and its history.""" try: diff --git a/backend/open_webui/models/shared_chats.py b/backend/open_webui/models/shared_chats.py index a6ceebb8b0..e0f3b8ba9c 100644 --- a/backend/open_webui/models/shared_chats.py +++ b/backend/open_webui/models/shared_chats.py @@ -119,17 +119,6 @@ class SharedChatsTable: return SharedChatModel.model_validate(shared_chat) return None - async def get_by_chat_id(self, chat_id: str, db: Optional[AsyncSession] = None) -> Optional[SharedChatModel]: - """Get the shared chat for a given original chat. Returns the most recent one.""" - async with get_async_db_context(db) as db: - result = await db.execute( - select(SharedChat).filter_by(chat_id=chat_id).order_by(SharedChat.updated_at.desc()).limit(1) - ) - shared_chat = result.scalars().first() - if shared_chat: - return SharedChatModel.model_validate(shared_chat) - return None - async def get_by_user_id( self, user_id: str, diff --git a/backend/open_webui/models/skills.py b/backend/open_webui/models/skills.py index cff4778394..0aa5f03472 100644 --- a/backend/open_webui/models/skills.py +++ b/backend/open_webui/models/skills.py @@ -154,15 +154,6 @@ class SkillsTable: except Exception: return None - async def get_skill_by_name(self, name: str, db: Optional[AsyncSession] = None) -> Optional[SkillModel]: - try: - async with get_async_db_context(db) as db: - result = await db.execute(select(Skill).filter_by(name=name)) - skill = result.scalars().first() - return await self._to_skill_model(skill, db=db) if skill else None - except Exception: - return None - async def get_skills( self, user_id: str | None = None, diff --git a/backend/open_webui/models/tags.py b/backend/open_webui/models/tags.py index 9f24d274bb..61917a1274 100644 --- a/backend/open_webui/models/tags.py +++ b/backend/open_webui/models/tags.py @@ -50,24 +50,6 @@ class TagChatIdForm(BaseModel): class TagTable: - async def insert_new_tag( - self, - name: str, - user_id: str, - db: AsyncSession | None = None, - ) -> TagModel | None: - """Create a new tag, deriving the id from the name.""" - async with get_async_db_context(db) as db: - tag_id = name.replace(' ', '_').lower() - try: - record = Tag(id=tag_id, user_id=user_id, name=name) - db.add(record) - await db.commit() - return TagModel.model_validate(record) if record else None - except Exception as e: - log.exception('Error inserting tag %r: %s', name, e) - return None # insertion failed - async def get_tag_by_name_and_user_id( self, name: str, user_id: str, db: AsyncSession | None = None ) -> TagModel | None: diff --git a/backend/open_webui/models/users.py b/backend/open_webui/models/users.py index abbbe122ea..e2df82142b 100644 --- a/backend/open_webui/models/users.py +++ b/backend/open_webui/models/users.py @@ -559,15 +559,6 @@ class UsersTable: row = (await session.execute(stmt)).scalars().first() return UserModel.model_validate(row) if row else None - async def get_num_users_active_today(self, db: AsyncSession | None = None) -> int | None: - async with get_async_db_context(db) as session: - current_timestamp = int(time.time()) - today_midnight_timestamp = current_timestamp - (current_timestamp % 86400) - result = await session.execute( - select(func.count()).select_from(User).where(User.last_active_at > today_midnight_timestamp) - ) - return result.scalar() - async def update_user_role_by_id(self, id: str, role: str, db: AsyncSession | None = None) -> UserModel | None: async with get_async_db_context(db) as session: user = await session.get(User, id)