diff --git a/backend/open_webui/models/users.py b/backend/open_webui/models/users.py index 3ba66f76d2..ba56b74ece 100644 --- a/backend/open_webui/models/users.py +++ b/backend/open_webui/models/users.py @@ -170,7 +170,13 @@ class UserGroupIdsListResponse(BaseModel): total: int -class UserInfoResponse(BaseModel): +class UserStatus(BaseModel): + status_emoji: Optional[str] = None + status_message: Optional[str] = None + status_expires_at: Optional[int] = None + + +class UserInfoResponse(UserStatus): id: str name: str email: str @@ -493,6 +499,21 @@ class UsersTable: except Exception: return None + def update_user_status_by_id( + self, id: str, form_data: UserStatus + ) -> Optional[UserModel]: + try: + with get_db() as db: + db.query(User).filter_by(id=id).update( + {**form_data.model_dump(exclude_none=True)} + ) + db.commit() + + user = db.query(User).filter_by(id=id).first() + return UserModel.model_validate(user) + except Exception: + return None + def update_user_profile_image_url_by_id( self, id: str, profile_image_url: str ) -> Optional[UserModel]: diff --git a/backend/open_webui/routers/auths.py b/backend/open_webui/routers/auths.py index 1b79d84cfd..42302043ed 100644 --- a/backend/open_webui/routers/auths.py +++ b/backend/open_webui/routers/auths.py @@ -17,7 +17,12 @@ from open_webui.models.auths import ( SignupForm, UpdatePasswordForm, ) -from open_webui.models.users import UserProfileImageResponse, Users, UpdateProfileForm +from open_webui.models.users import ( + UserProfileImageResponse, + Users, + UpdateProfileForm, + UserStatus, +) from open_webui.models.groups import Groups from open_webui.models.oauth_sessions import OAuthSessions @@ -82,7 +87,7 @@ class SessionUserResponse(Token, UserProfileImageResponse): permissions: Optional[dict] = None -class SessionUserInfoResponse(SessionUserResponse): +class SessionUserInfoResponse(SessionUserResponse, UserStatus): bio: Optional[str] = None gender: Optional[str] = None date_of_birth: Optional[datetime.date] = None @@ -139,6 +144,9 @@ async def get_session_user( "bio": user.bio, "gender": user.gender, "date_of_birth": user.date_of_birth, + "status_emoji": user.status_emoji, + "status_message": user.status_message, + "status_expires_at": user.status_expires_at, "permissions": user_permissions, } diff --git a/backend/open_webui/routers/users.py b/backend/open_webui/routers/users.py index c51916422f..3c1bbb72a8 100644 --- a/backend/open_webui/routers/users.py +++ b/backend/open_webui/routers/users.py @@ -21,6 +21,7 @@ from open_webui.models.users import ( UserInfoListResponse, UserInfoListResponse, UserRoleUpdateForm, + UserStatus, Users, UserSettings, UserUpdateForm, @@ -299,6 +300,43 @@ async def update_user_settings_by_session_user( ) +############################ +# GetUserStatusBySessionUser +############################ + + +@router.get("/user/status") +async def get_user_status_by_session_user(user=Depends(get_verified_user)): + user = Users.get_user_by_id(user.id) + if user: + return user + else: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail=ERROR_MESSAGES.USER_NOT_FOUND, + ) + + +############################ +# UpdateUserStatusBySessionUser +############################ + + +@router.post("/user/status/update") +async def update_user_status_by_session_user( + form_data: UserStatus, user=Depends(get_verified_user) +): + user = Users.get_user_by_id(user.id) + if user: + user = Users.update_user_status_by_id(user.id, form_data) + return user + else: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail=ERROR_MESSAGES.USER_NOT_FOUND, + ) + + ############################ # GetUserInfoBySessionUser ############################ @@ -350,9 +388,10 @@ async def update_user_info_by_session_user( ############################ -class UserActiveResponse(BaseModel): +class UserActiveResponse(UserStatus): name: str profile_image_url: Optional[str] = None + is_active: bool model_config = ConfigDict(extra="allow") @@ -377,8 +416,7 @@ async def get_user_by_id(user_id: str, user=Depends(get_verified_user)): if user: return UserActiveResponse( **{ - "id": user.id, - "name": user.name, + **user.model_dump(), "is_active": Users.is_user_active(user_id), } ) diff --git a/src/lib/apis/users/index.ts b/src/lib/apis/users/index.ts index 89e2daa104..d6da54bbf9 100644 --- a/src/lib/apis/users/index.ts +++ b/src/lib/apis/users/index.ts @@ -327,6 +327,36 @@ export const getUserById = async (token: string, userId: string) => { return res; }; +export const updateUserStatus = async (token: string, formData: object) => { + let error = null; + + const res = await fetch(`${WEBUI_API_BASE_URL}/users/user/status/update`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${token}` + }, + body: JSON.stringify({ + ...formData + }) + }) + .then(async (res) => { + if (!res.ok) throw await res.json(); + return res.json(); + }) + .catch((err) => { + console.error(err); + error = err.detail; + return null; + }); + + if (error) { + throw error; + } + + return res; +}; + export const getUserInfo = async (token: string) => { let error = null; const res = await fetch(`${WEBUI_API_BASE_URL}/users/user/info`, { diff --git a/src/lib/components/channel/Messages/Message/UserStatus.svelte b/src/lib/components/channel/Messages/Message/UserStatus.svelte index 07efa89c43..d04c9eb291 100644 --- a/src/lib/components/channel/Messages/Message/UserStatus.svelte +++ b/src/lib/components/channel/Messages/Message/UserStatus.svelte @@ -11,6 +11,8 @@ import ChatBubble from '$lib/components/icons/ChatBubble.svelte'; import ChatBubbleOval from '$lib/components/icons/ChatBubbleOval.svelte'; import { goto } from '$app/navigation'; + import Emoji from '$lib/components/common/Emoji.svelte'; + import Tooltip from '$lib/components/common/Tooltip.svelte'; export let user = null; @@ -71,6 +73,25 @@ + {#if user?.status_emoji || user?.status_message} +