diff --git a/backend/apps/web/models/auths.py b/backend/apps/web/models/auths.py index 1bd4b4cb..800750c3 100644 --- a/backend/apps/web/models/auths.py +++ b/backend/apps/web/models/auths.py @@ -64,6 +64,11 @@ class SigninForm(BaseModel): password: str +class UpdatePasswordForm(BaseModel): + password: str + new_password: str + + class SignupForm(BaseModel): name: str email: str @@ -109,7 +114,16 @@ class AuthsTable: except: return None - def delete_auth_by_id(self, id: str) -> Optional[UserModel]: + def update_user_password_by_id(self, id: str, new_password: str) -> bool: + try: + query = Auth.update(password=new_password).where(Auth.id == id) + result = query.execute() + + return True if result == 1 else False + except: + return False + + def delete_auth_by_id(self, id: str) -> bool: try: # Delete User result = Users.delete_user_by_id(id) diff --git a/backend/apps/web/routers/auths.py b/backend/apps/web/routers/auths.py index 27d6a3b6..9174865a 100644 --- a/backend/apps/web/routers/auths.py +++ b/backend/apps/web/routers/auths.py @@ -11,6 +11,7 @@ import uuid from apps.web.models.auths import ( SigninForm, SignupForm, + UpdatePasswordForm, UserResponse, SigninResponse, Auths, @@ -53,6 +54,28 @@ async def get_session_user(cred=Depends(bearer_scheme)): ) +############################ +# Update Password +############################ + + +@router.post("/update/password", response_model=bool) +async def update_password(form_data: UpdatePasswordForm, cred=Depends(bearer_scheme)): + token = cred.credentials + session_user = Users.get_user_by_token(token) + + if session_user: + user = Auths.authenticate_user(session_user.email, form_data.password) + + if user: + hashed = get_password_hash(form_data.new_password) + return Auths.update_user_password_by_id(user.id, hashed) + else: + raise HTTPException(400, detail=ERROR_MESSAGES.INVALID_PASSWORD) + else: + raise HTTPException(400, detail=ERROR_MESSAGES.INVALID_CRED) + + ############################ # SignIn ############################ diff --git a/backend/constants.py b/backend/constants.py index a068995d..761507f2 100644 --- a/backend/constants.py +++ b/backend/constants.py @@ -21,6 +21,9 @@ class ERROR_MESSAGES(str, Enum): "Your session has expired or the token is invalid. Please sign in again." ) INVALID_CRED = "The email or password provided is incorrect. Please check for typos and try logging in again." + INVALID_PASSWORD = ( + "The password provided is incorrect. Please check for typos and try again." + ) UNAUTHORIZED = "401 Unauthorized" ACCESS_PROHIBITED = "You do not have permission to access this resource. Please contact your administrator for assistance." ACTION_PROHIBITED = ( diff --git a/src/lib/apis/auths/index.ts b/src/lib/apis/auths/index.ts index 56a4a7a6..73934055 100644 --- a/src/lib/apis/auths/index.ts +++ b/src/lib/apis/auths/index.ts @@ -88,3 +88,34 @@ export const userSignUp = async (name: string, email: string, password: string) return res; }; + +export const updateUserPassword = async (token: string, password: string, newPassword: string) => { + let error = null; + + const res = await fetch(`${WEBUI_API_BASE_URL}/auths/update/password`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + ...(token && { authorization: `Bearer ${token}` }) + }, + body: JSON.stringify({ + password: password, + new_password: newPassword + }) + }) + .then(async (res) => { + if (!res.ok) throw await res.json(); + return res.json(); + }) + .catch((err) => { + console.log(err); + error = err.detail; + return null; + }); + + if (error) { + throw error; + } + + return res; +}; diff --git a/src/lib/components/chat/SettingsModal.svelte b/src/lib/components/chat/SettingsModal.svelte index 47c57d99..18aa6eca 100644 --- a/src/lib/components/chat/SettingsModal.svelte +++ b/src/lib/components/chat/SettingsModal.svelte @@ -18,6 +18,7 @@ import Advanced from './Settings/Advanced.svelte'; import Modal from '../common/Modal.svelte'; + import { updateUserPassword } from '$lib/apis/auths'; export let show = false; @@ -118,6 +119,11 @@ let authType = 'Basic'; let authContent = ''; + // Account + let currentPassword = ''; + let newPassword = ''; + let newPasswordConfirm = ''; + // About let ollamaVersion = ''; @@ -595,6 +601,31 @@ return models; }; + const updatePasswordHandler = async () => { + if (newPassword === newPasswordConfirm) { + const res = await updateUserPassword(localStorage.token, currentPassword, newPassword).catch( + (error) => { + toast.error(error); + return null; + } + ); + + if (res) { + toast.success('Successfully updated.'); + } + + currentPassword = ''; + newPassword = ''; + newPasswordConfirm = ''; + } else { + toast.error( + `The passwords you entered don't quite match. Please double-check and try again.` + ); + newPassword = ''; + newPasswordConfirm = ''; + } + }; + onMount(async () => { let settings = JSON.parse(localStorage.getItem('settings') ?? '{}'); console.log(settings); @@ -845,6 +876,32 @@ {/if} + + + + {:else if selectedTab === 'about'}