This commit is contained in:
Timothy Jaeryang Baek committed 2026-05-21 14:01:57 +04:00
1 parent cac4c6da2e
commit 260ead64da
19 files changed
+772 -771

No files matched your search

+6 -11
View File
@@ -513,25 +513,20 @@ async def delete_all_user_chats(
@router.get('/list/user/{user_id}', response_model=list[ChatTitleIdResponse])
async def get_user_chat_list_by_user_id(
user_id: str,
page: int | None = None,
query: str | None = None,
order_by: str | None = None,
direction: str | None = None,
user=Depends(get_admin_user),
db: AsyncSession = Depends(get_async_session),
user_id: str, page: int | None = None, query: str | None = None,
order_by: str | None = None, direction: str | None = None,
user=Depends(get_admin_user), db: AsyncSession = Depends(get_async_session),
):
"""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,
)
if page is None:
page = 1
effective_page = page if page is not None else 1
limit = 60
skip = (page - 1) * limit
skip = (effective_page - 1) * limit
filter = {}
if query:
+7 -7
View File
@@ -62,14 +62,14 @@ class MemoryUpdateModel(BaseModel):
@router.post('/add', response_model=MemoryModel | None)
async def add_memory(
request: Request,
form_data: AddMemoryForm,
user=Depends(get_verified_user),
request: Request, form_data: AddMemoryForm, user=Depends(get_verified_user),
):
# NOTE: We intentionally do NOT use Depends(get_async_session) here.
# Database operations (insert_new_memory) manage their own short-lived sessions.
# This prevents holding a connection during EMBEDDING_FUNCTION()
# which makes external embedding API calls (1-5+ seconds).
"""Persist a new memory and embed it into the user's vector collection.
Does NOT use ``Depends(get_async_session)`` — database operations manage their
own short-lived sessions so a connection is not held during the external
embedding API call (``EMBEDDING_FUNCTION``), which can take 1-5+ seconds.
"""
if not request.app.state.config.ENABLE_MEMORIES:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
+7 -10
View File
@@ -193,11 +193,10 @@ async def get_model_tags(user=Depends(get_verified_user), db: AsyncSession = Dep
@router.post('/create', response_model=ModelModel | None)
async def create_new_model(
request: Request,
form_data: ModelForm,
user=Depends(get_verified_user),
db: AsyncSession = Depends(get_async_session),
request: Request, form_data: ModelForm,
user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session),
):
"""Create a new workspace model entry."""
if user.role != 'admin' and not await has_permission(
user.id, 'workspace.models', request.app.state.config.USER_PERMISSIONS, db=db
):
@@ -579,16 +578,14 @@ async def toggle_model_by_id(id: str, user=Depends(get_verified_user), db: Async
@router.post('/model/update', response_model=ModelModel | None)
async def update_model_by_id(
request: Request,
form_data: ModelForm,
user=Depends(get_verified_user),
db: AsyncSession = Depends(get_async_session),
request: Request, form_data: ModelForm,
user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session),
):
"""Update a workspace model's configuration."""
model = await Models.get_model_by_id(form_data.id, db=db)
if not model:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail=ERROR_MESSAGES.NOT_FOUND,
status_code=status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.NOT_FOUND,
)
if (
+3 -5
View File
@@ -239,12 +239,10 @@ async def get_prompt_by_id(
@router.post('/id/{prompt_id}/update', response_model=PromptModel | None)
async def update_prompt_by_id(
request: Request,
prompt_id: str,
form_data: PromptForm,
user=Depends(get_verified_user),
db: AsyncSession = Depends(get_async_session),
request: Request, prompt_id: str, form_data: PromptForm,
user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session),
):
"""Update a prompt's content, creating a new history entry if changed."""
prompt = await Prompts.get_prompt_by_id(prompt_id, db=db)
if not prompt:
+7 -11
View File
@@ -324,11 +324,10 @@ async def export_tools(
@router.post('/create', response_model=ToolResponse | None)
async def create_new_tools(
request: Request,
form_data: ToolForm,
user=Depends(get_verified_user),
db: AsyncSession = Depends(get_async_session),
request: Request, form_data: ToolForm,
user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session),
):
"""Create a new tool from user-supplied Python source code."""
if user.role != 'admin' and not (
await has_permission(user.id, 'workspace.tools', request.app.state.config.USER_PERMISSIONS, db=db)
or await has_permission(
@@ -449,17 +448,14 @@ async def get_tools_by_id(id: str, user=Depends(get_verified_user), db: AsyncSes
@router.post('/id/{id}/update', response_model=ToolModel | None)
async def update_tools_by_id(
request: Request,
id: str,
form_data: ToolForm,
user=Depends(get_verified_user),
db: AsyncSession = Depends(get_async_session),
request: Request, id: str, form_data: ToolForm,
user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session),
):
"""Update an existing tool's source code and metadata."""
tools = await Tools.get_tool_by_id(id, db=db)
if not tools:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail=ERROR_MESSAGES.NOT_FOUND,
status_code=status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.NOT_FOUND,
)
# Is the user the original creator, in a group with write access, or an admin
+11 -10
View File
@@ -384,21 +384,22 @@ async def get_user_info_by_session_user(user=Depends(get_verified_user), db: Asy
@router.post('/user/info/update', response_model=dict | None)
async def update_user_info_by_session_user(
form_data: dict, user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session)
form_data: dict, user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session),
):
# Merges against the auth-time snapshot of user.info. The previous pre-merge
# refetch only narrowed (did not eliminate) the lost-update window on concurrent
# same-user writes; real safety needs row locking or a version column.
existing_info = user.info or {}
updated = await Users.update_user_by_id(user.id, {'info': {**existing_info, **form_data}}, db=db)
if updated:
return updated.info
else:
"""Merge caller-supplied fields into the current user's info dict.
Uses the auth-time snapshot of ``user.info`` as the merge base. This does
NOT eliminate lost-update races on concurrent same-user writes; real safety
would need row locking or an optimistic-concurrency version column.
"""
merged_info = {**(user.info or {}), **form_data}
updated = await Users.update_user_by_id(user.id, {'info': merged_info}, db=db)
if not updated:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=ERROR_MESSAGES.USER_NOT_FOUND,
)
return updated.info
############################
# GetUserById
+5 -2
View File
@@ -94,18 +94,21 @@ async def download_chat_as_pdf(form_data: ChatTitleMessagesForm, user=Depends(ge
@router.get('/db/download')
async def download_db(user=Depends(get_admin_user)):
"""Download the raw SQLite database file (admin-only, SQLite deployments only)."""
if not ENABLE_ADMIN_EXPORT:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
)
from open_webui.internal.db import engine
if engine.name != 'sqlite':
from open_webui.internal.db import engine # deferred import
if engine.name != 'sqlite': # only SQLite DBs can be downloaded as a file
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=ERROR_MESSAGES.DB_NOT_SQLITE,
)
return FileResponse(
engine.url.database,
media_type='application/octet-stream',