From 470a074cd14ca3eedfe462c72aec21d5cf047821 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Thu, 21 May 2026 16:56:56 +0400 Subject: [PATCH] refac --- backend/open_webui/config.py | 4 +- backend/open_webui/main.py | 19 ++--- backend/open_webui/models/models.py | 23 +++--- backend/open_webui/routers/auths.py | 5 +- backend/open_webui/routers/chats.py | 93 +++++++++---------------- backend/open_webui/routers/retrieval.py | 15 ++-- 6 files changed, 58 insertions(+), 101 deletions(-) diff --git a/backend/open_webui/config.py b/backend/open_webui/config.py index fd20c0c41e..67a76b1e54 100644 --- a/backend/open_webui/config.py +++ b/backend/open_webui/config.py @@ -199,9 +199,7 @@ if frontend_loader.exists(): logging.error(f'An error occurred: {e}') -#################################### -# STORAGE PROVIDER -#################################### +# --- Storage Provider --- STORAGE_PROVIDER = os.getenv('STORAGE_PROVIDER', 'local') # defaults to local, s3 STORAGE_LOCAL_CACHE = os.getenv('STORAGE_LOCAL_CACHE', 'true').lower() == 'true' diff --git a/backend/open_webui/main.py b/backend/open_webui/main.py index b06adce68a..49021ca667 100644 --- a/backend/open_webui/main.py +++ b/backend/open_webui/main.py @@ -2562,9 +2562,7 @@ async def get_current_usage(user=Depends(get_verified_user)): raise HTTPException(status_code=500, detail='Internal Server Error') -############################ -# OAuth Login & Callback -############################ +# --- OAuth Login & Callback --- # Initialize OAuth client manager with any MCP tool servers using OAuth 2.1 @@ -2753,12 +2751,6 @@ async def oauth_login(provider: str, request: Request): return await oauth_manager.handle_login(request, provider) -# OAuth login logic is as follows: -# 1. Attempt to find a user with matching subject ID, tied to the provider -# 2. If OAUTH_MERGE_ACCOUNTS_BY_EMAIL is true, find a user with the email address provided via OAuth -# - This is considered insecure in general, as OAuth providers do not always verify email addresses -# 3. If there is no user, and ENABLE_OAUTH_SIGNUP is true, create a user -# - Email addresses are considered unique, so we fail registration if the email address is already taken @app.get('/oauth/{provider}/login/callback') @app.get('/oauth/{provider}/callback') # Legacy endpoint async def oauth_login_callback( @@ -2767,6 +2759,15 @@ async def oauth_login_callback( response: Response, db: AsyncSession = Depends(get_async_session), ): + """Handle the OAuth provider callback. + + Resolution order: + 1. Match by subject ID bound to the provider. + 2. If ``OAUTH_MERGE_ACCOUNTS_BY_EMAIL`` is enabled, match by email + (note: some providers do not verify email addresses). + 3. If no match and ``ENABLE_OAUTH_SIGNUP`` is enabled, create a new user + (fails if the email is already registered). + """ return await oauth_manager.handle_callback(request, provider, response, db=db) diff --git a/backend/open_webui/models/models.py b/backend/open_webui/models/models.py index 896442d1c4..43efa561ea 100755 --- a/backend/open_webui/models/models.py +++ b/backend/open_webui/models/models.py @@ -17,28 +17,20 @@ from sqlalchemy.ext.asyncio import AsyncSession log = logging.getLogger(__name__) -#################### -# Models DB Schema -# A misconfigured model wastes the time of everyone -# who trusts it. Let what is set here be set with care. -#################### +# --- Models DB Schema --- -# ModelParams is a model for the data stored in the params field of the Model table class ModelParams(BaseModel): + """Parameters for model inference (temperature, top_p, etc.).""" + model_config = ConfigDict(extra='allow') - pass -# ModelMeta is a model for the data stored in the meta field of the Model table class ModelMeta(BaseModel): + """Metadata for a workspace model entry (profile, description, tags, capabilities).""" + profile_image_url: str | None = '/static/favicon.png' - - description: str | None = None - """ - User-facing description of the model. - """ - + description: str | None = Field(default=None, description='User-facing description of the model.') capabilities: dict | None = None model_config = ConfigDict(extra='allow') @@ -59,7 +51,8 @@ class ModelMeta(BaseModel): return data -class Model(Base): # provider model config +class Model(Base): + """Workspace model entry — wraps an upstream LLM with custom params and metadata.""" __tablename__ = 'model' diff --git a/backend/open_webui/routers/auths.py b/backend/open_webui/routers/auths.py index b3a45a58f3..1dc280c858 100644 --- a/backend/open_webui/routers/auths.py +++ b/backend/open_webui/routers/auths.py @@ -286,8 +286,9 @@ async def update_password( session_user=Depends(get_current_user), db: AsyncSession = Depends(get_async_session), ): + # Trusted-header auth mode delegates passwords to the reverse proxy if WEBUI_AUTH_TRUSTED_EMAIL_HEADER: - raise HTTPException(400, detail=ERROR_MESSAGES.ACTION_PROHIBITED) + raise HTTPException(status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.ACTION_PROHIBITED) if session_user: user = await Auths.authenticate_user( session_user.email, @@ -578,7 +579,7 @@ async def signin( if WEBUI_AUTH_TRUSTED_EMAIL_HEADER: if WEBUI_AUTH_TRUSTED_EMAIL_HEADER not in request.headers: - raise HTTPException(400, detail=ERROR_MESSAGES.INVALID_TRUSTED_HEADER) + raise HTTPException(status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.INVALID_TRUSTED_HEADER) email = request.headers[WEBUI_AUTH_TRUSTED_EMAIL_HEADER].lower() name = email diff --git a/backend/open_webui/routers/chats.py b/backend/open_webui/routers/chats.py index 66e4f402c8..91cdb21e78 100644 --- a/backend/open_webui/routers/chats.py +++ b/backend/open_webui/routers/chats.py @@ -519,10 +519,7 @@ async def get_user_chat_list_by_user_id( ): """List chat summaries for a given user (admin-only endpoint).""" if not ENABLE_ADMIN_CHAT_ACCESS: - raise HTTPException( - status_code=status.HTTP_401_UNAUTHORIZED, - detail=ERROR_MESSAGES.ACCESS_PROHIBITED, - ) + raise HTTPException(status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.ACCESS_PROHIBITED) effective_page = page if page is not None else 1 limit = 60 @@ -757,10 +754,7 @@ async def get_all_user_tags(user=Depends(get_verified_user), db: AsyncSession = @router.get('/all/db', response_model=list[ChatResponse]) async def get_all_user_chats_in_db(user=Depends(get_admin_user), db: AsyncSession = Depends(get_async_session)): if not ENABLE_ADMIN_EXPORT: - raise HTTPException( - status_code=status.HTTP_401_UNAUTHORIZED, - detail=ERROR_MESSAGES.ACCESS_PROHIBITED, - ) + raise HTTPException(status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.ACCESS_PROHIBITED) return [ChatResponse(**chat.model_dump()) for chat in await Chats.get_chats(db=db)] @@ -1326,9 +1320,7 @@ async def archive_chat_by_id(id: str, user=Depends(get_verified_user), db: Async raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.DEFAULT()) -############################ -# ShareChatById -############################ +# --- Share Chat --- @router.post('/{id}/share', response_model=ChatResponse | None) @@ -1338,51 +1330,35 @@ async def share_chat_by_id( user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session), ): - if (user.role != 'admin') and ( - not await has_permission(user.id, 'chat.share', request.app.state.config.USER_PERMISSIONS) + if user.role != 'admin' and not await has_permission( + user.id, 'chat.share', request.app.state.config.USER_PERMISSIONS ): - raise HTTPException( - status_code=status.HTTP_401_UNAUTHORIZED, - detail=ERROR_MESSAGES.ACCESS_PROHIBITED, - ) + raise HTTPException(status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.ACCESS_PROHIBITED) chat = await Chats.get_chat_by_id_and_user_id(id, user.id, db=db) + if not chat: + raise HTTPException(status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.ACCESS_PROHIBITED) - if chat: - if chat.share_id: - # Re-snapshot existing share - shared = await SharedChats.update(chat.share_id, db=db) - if shared: - # Re-fetch the original chat to return - chat = await Chats.get_chat_by_id(id, db=db) - return ChatResponse(**chat.model_dump()) + # If a share already exists, re-snapshot it + if chat.share_id: + shared = await SharedChats.update(chat.share_id, db=db) + if shared: + chat = await Chats.get_chat_by_id(id, db=db) + return ChatResponse(**chat.model_dump()) - # Create new share - shared = await SharedChats.create(id, user.id, db=db) - if not shared: - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=ERROR_MESSAGES.DEFAULT(), - ) - # Set share_id on the original chat - chat = await Chats.update_chat_share_id_by_id(id, shared.id, db=db) - if not chat: - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=ERROR_MESSAGES.DEFAULT(), - ) - return ChatResponse(**chat.model_dump()) + # Create a new share + shared = await SharedChats.create(id, user.id, db=db) + if not shared: + raise HTTPException(status.HTTP_500_INTERNAL_SERVER_ERROR, detail=ERROR_MESSAGES.DEFAULT()) - else: - raise HTTPException( - status_code=status.HTTP_401_UNAUTHORIZED, - detail=ERROR_MESSAGES.ACCESS_PROHIBITED, - ) + chat = await Chats.update_chat_share_id_by_id(id, shared.id, db=db) + if not chat: + raise HTTPException(status.HTTP_500_INTERNAL_SERVER_ERROR, detail=ERROR_MESSAGES.DEFAULT()) + + return ChatResponse(**chat.model_dump()) -############################ -# DeleteSharedChatById -############################ +# --- Delete Shared Chat --- @router.delete('/{id}/share', response_model=bool | None) @@ -1390,22 +1366,17 @@ async def delete_shared_chat_by_id( id: str, user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session) ): chat = await Chats.get_chat_by_id_and_user_id(id, user.id, db=db) - if chat: - if not chat.share_id: - return False + if not chat: + raise HTTPException(status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.ACCESS_PROHIBITED) - await SharedChats.delete_by_chat_id(id, db=db) - await Chats.update_chat_share_id_by_id(id, None, db=db) + if not chat.share_id: + return False - # Revoke all access grants for this shared chat - await AccessGrants.set_access_grants('shared_chat', id, [], db=db) + await SharedChats.delete_by_chat_id(id, db=db) + await Chats.update_chat_share_id_by_id(id, None, db=db) + await AccessGrants.set_access_grants('shared_chat', id, [], db=db) - return True - else: - raise HTTPException( - status_code=status.HTTP_401_UNAUTHORIZED, - detail=ERROR_MESSAGES.ACCESS_PROHIBITED, - ) + return True ############################ diff --git a/backend/open_webui/routers/retrieval.py b/backend/open_webui/routers/retrieval.py index b8e7299e4e..f9a27b2529 100644 --- a/backend/open_webui/routers/retrieval.py +++ b/backend/open_webui/routers/retrieval.py @@ -2258,12 +2258,8 @@ async def process_web_search(request: Request, form_data: SearchForm, user=Depen log.debug(f'urls: {urls}') except Exception as e: - log.exception(e) - - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail=ERROR_MESSAGES.WEB_SEARCH_ERROR(e), - ) + log.exception('Web search failed') + raise HTTPException(status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.WEB_SEARCH_ERROR(e)) if len(urls) == 0: raise HTTPException( @@ -2343,11 +2339,8 @@ async def process_web_search(request: Request, form_data: SearchForm, user=Depen 'loaded_count': len(docs), } except Exception as e: - log.exception(e) - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail=ERROR_MESSAGES.DEFAULT(e), - ) + log.exception('Web search content loading failed') + raise HTTPException(status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.DEFAULT(e)) async def _validate_collection_access(collection_names: list[str], user, access_type: str = 'read') -> None: