diff --git a/backend/apps/web/models/auths.py b/backend/apps/web/models/auths.py index 2124e6208..367db3ff7 100644 --- a/backend/apps/web/models/auths.py +++ b/backend/apps/web/models/auths.py @@ -67,6 +67,11 @@ class ProfileImageUrlForm(BaseModel): profile_image_url: str +class UpdateProfileForm(BaseModel): + profile_image_url: str + name: str + + class UpdatePasswordForm(BaseModel): password: str new_password: str diff --git a/backend/apps/web/routers/auths.py b/backend/apps/web/routers/auths.py index 6a2f3895f..f45c67ac2 100644 --- a/backend/apps/web/routers/auths.py +++ b/backend/apps/web/routers/auths.py @@ -11,7 +11,7 @@ import uuid from apps.web.models.auths import ( SigninForm, SignupForm, - ProfileImageUrlForm, + UpdateProfileForm, UpdatePasswordForm, UserResponse, SigninResponse, @@ -42,17 +42,18 @@ async def get_session_user(user=Depends(get_current_user)): ############################ -# Update Profile Image Url +# Update Profile ############################ @router.post("/update/profile", response_model=UserResponse) -async def update_profile_image_url( - form_data: ProfileImageUrlForm, session_user=Depends(get_current_user) +async def update_profile( + form_data: UpdateProfileForm, session_user=Depends(get_current_user) ): if session_user: - user = Users.update_user_profile_image_url_by_id( - session_user.id, form_data.profile_image_url + user = Users.update_user_by_id( + session_user.id, + {"profile_image_url": form_data.profile_image_url, "name": form_data.name}, ) if user: return user diff --git a/src/lib/apis/auths/index.ts b/src/lib/apis/auths/index.ts index 8734a5885..5f16f83f5 100644 --- a/src/lib/apis/auths/index.ts +++ b/src/lib/apis/auths/index.ts @@ -89,6 +89,37 @@ export const userSignUp = async (name: string, email: string, password: string) return res; }; +export const updateUserProfile = async (token: string, name: string, profileImageUrl: string) => { + let error = null; + + const res = await fetch(`${WEBUI_API_BASE_URL}/auths/update/profile`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + ...(token && { authorization: `Bearer ${token}` }) + }, + body: JSON.stringify({ + name: name, + profile_image_url: profileImageUrl + }) + }) + .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; +}; + export const updateUserPassword = async (token: string, password: string, newPassword: string) => { let error = null; diff --git a/src/lib/components/chat/Settings/Account.svelte b/src/lib/components/chat/Settings/Account.svelte new file mode 100644 index 000000000..e9196a1f2 --- /dev/null +++ b/src/lib/components/chat/Settings/Account.svelte @@ -0,0 +1,168 @@ + + +
+
+ { + const files = e?.target?.files ?? []; + let reader = new FileReader(); + reader.onload = (event) => { + let originalImageUrl = `${event.target.result}`; + + const img = new Image(); + img.src = originalImageUrl; + + img.onload = function () { + const canvas = document.createElement('canvas'); + const ctx = canvas.getContext('2d'); + + // Calculate the aspect ratio of the image + const aspectRatio = img.width / img.height; + + // Calculate the new width and height to fit within 100x100 + let newWidth, newHeight; + if (aspectRatio > 1) { + newWidth = 100 * aspectRatio; + newHeight = 100; + } else { + newWidth = 100; + newHeight = 100 / aspectRatio; + } + + // Set the canvas size + canvas.width = 100; + canvas.height = 100; + + // Calculate the position to center the image + const offsetX = (100 - newWidth) / 2; + const offsetY = (100 - newHeight) / 2; + + // Draw the image on the canvas + ctx.drawImage(img, offsetX, offsetY, newWidth, newHeight); + + // Get the base64 representation of the compressed image + const compressedSrc = canvas.toDataURL('image/jpeg'); + + // Display the compressed image + profileImageUrl = compressedSrc; + + e.target.files = null; + }; + }; + + if ( + files.length > 0 && + ['image/gif', 'image/jpeg', 'image/png'].includes(files[0]['type']) + ) { + reader.readAsDataURL(files[0]); + } + }} + /> + +
Profile
+ +
+
+ +
+ +
+
+
Name
+ +
+ +
+
+
+
+ +
+ +
+ +
+ +
+
diff --git a/src/lib/components/chat/Settings/Account/UpdatePassword.svelte b/src/lib/components/chat/Settings/Account/UpdatePassword.svelte new file mode 100644 index 000000000..38c250288 --- /dev/null +++ b/src/lib/components/chat/Settings/Account/UpdatePassword.svelte @@ -0,0 +1,106 @@ + + +
{ + updatePasswordHandler(); + }} +> +
+
Change Password
+ +
+ + {#if show} +
+
+
Current Password
+ +
+ +
+
+ +
+
New Password
+ +
+ +
+
+ +
+
Confirm Password
+ +
+ +
+
+
+ +
+ +
+ {/if} +
diff --git a/src/lib/components/chat/SettingsModal.svelte b/src/lib/components/chat/SettingsModal.svelte index f04e9e5f0..e348c807a 100644 --- a/src/lib/components/chat/SettingsModal.svelte +++ b/src/lib/components/chat/SettingsModal.svelte @@ -36,6 +36,8 @@ import { resetVectorDB } from '$lib/apis/rag'; import { setDefaultPromptSuggestions } from '$lib/apis/configs'; import { getBackendConfig } from '$lib/apis'; + import UpdatePassword from './Settings/Account/UpdatePassword.svelte'; + import Account from './Settings/Account.svelte'; export let show = false; @@ -126,6 +128,7 @@ let authContent = ''; // Account + let profileImageUrl = ''; let currentPassword = ''; let newPassword = ''; let newPasswordConfirm = ''; @@ -559,31 +562,6 @@ 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 () => { console.log('settings', $user.role === 'admin'); if ($user.role === 'admin') { @@ -616,7 +594,6 @@ responseAutoCopy = settings.responseAutoCopy ?? false; titleAutoGenerateModel = settings.titleAutoGenerateModel ?? ''; gravatarEmail = settings.gravatarEmail ?? ''; - speakVoice = settings.speakVoice ?? ''; const getVoicesLoop = setInterval(async () => { @@ -631,12 +608,6 @@ saveChatHistory = settings.saveChatHistory ?? true; - authEnabled = settings.authHeader !== undefined ? true : false; - if (authEnabled) { - authType = settings.authHeader.split(' ')[0]; - authContent = settings.authHeader.split(' ')[1]; - } - ollamaVersion = await getOllamaVersion(localStorage.token).catch((error) => { return ''; }); @@ -2040,184 +2011,12 @@ {/if} - {:else if selectedTab === 'auth'} -
{ - console.log('auth save'); - saveSettings({ - authHeader: authEnabled ? `${authType} ${authContent}` : undefined - }); + {:else if selectedTab === 'account'} + { show = false; }} - > -
-
-
-
Authorization Header
- - -
-
- - {#if authEnabled} -
- -
-
- - -
- -
-
-
- Toggle between 'Basic' - and 'Bearer' by - clicking on the label next to the input. -
-
- -
- -
-
Preview Authorization Header
-