mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-03-06 05:30:08 +00:00
[Auth] Allow change password in user panel (#3699)
Co-authored-by: github-actions <github-actions@github.com>
This commit is contained in:
47
src/components/dialog/content/UpdatePasswordContent.vue
Normal file
47
src/components/dialog/content/UpdatePasswordContent.vue
Normal file
@@ -0,0 +1,47 @@
|
||||
<template>
|
||||
<Form
|
||||
class="flex flex-col gap-6 w-96"
|
||||
:resolver="zodResolver(updatePasswordSchema)"
|
||||
@submit="onSubmit"
|
||||
>
|
||||
<PasswordFields />
|
||||
|
||||
<!-- Submit Button -->
|
||||
<Button
|
||||
type="submit"
|
||||
:label="$t('userSettings.updatePassword')"
|
||||
class="h-10 font-medium mt-4"
|
||||
:loading="loading"
|
||||
/>
|
||||
</Form>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Form, FormSubmitEvent } from '@primevue/forms'
|
||||
import { zodResolver } from '@primevue/forms/resolvers/zod'
|
||||
import Button from 'primevue/button'
|
||||
import { ref } from 'vue'
|
||||
|
||||
import PasswordFields from '@/components/dialog/content/signin/PasswordFields.vue'
|
||||
import { updatePasswordSchema } from '@/schemas/signInSchema'
|
||||
import { useFirebaseAuthService } from '@/services/firebaseAuthService'
|
||||
|
||||
const authService = useFirebaseAuthService()
|
||||
const loading = ref(false)
|
||||
|
||||
const { onSuccess } = defineProps<{
|
||||
onSuccess: () => void
|
||||
}>()
|
||||
|
||||
const onSubmit = async (event: FormSubmitEvent) => {
|
||||
if (event.valid) {
|
||||
loading.value = true
|
||||
try {
|
||||
await authService.updatePassword(event.values.password)
|
||||
onSuccess()
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -38,6 +38,17 @@
|
||||
<div class="text-muted flex items-center gap-1">
|
||||
<i :class="providerIcon" />
|
||||
{{ providerName }}
|
||||
<Button
|
||||
v-if="isEmailProvider"
|
||||
v-tooltip="{
|
||||
value: $t('userSettings.updatePassword'),
|
||||
showDelay: 300
|
||||
}"
|
||||
icon="pi pi-pen-to-square"
|
||||
severity="secondary"
|
||||
text
|
||||
@click="dialogService.showUpdatePasswordDialog()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -83,9 +94,11 @@ import ProgressSpinner from 'primevue/progressspinner'
|
||||
import TabPanel from 'primevue/tabpanel'
|
||||
import { computed } from 'vue'
|
||||
|
||||
import { useDialogService } from '@/services/dialogService'
|
||||
import { useCommandStore } from '@/stores/commandStore'
|
||||
import { useFirebaseAuthStore } from '@/stores/firebaseAuthStore'
|
||||
|
||||
const dialogService = useDialogService()
|
||||
const authStore = useFirebaseAuthStore()
|
||||
const commandStore = useCommandStore()
|
||||
const user = computed(() => authStore.currentUser)
|
||||
@@ -113,6 +126,11 @@ const providerIcon = computed(() => {
|
||||
return 'pi pi-user'
|
||||
})
|
||||
|
||||
const isEmailProvider = computed(() => {
|
||||
const providerId = user.value?.providerData[0]?.providerId
|
||||
return providerId === 'password'
|
||||
})
|
||||
|
||||
const handleSignOut = async () => {
|
||||
await commandStore.execute('Comfy.User.SignOut')
|
||||
}
|
||||
|
||||
111
src/components/dialog/content/signin/PasswordFields.vue
Normal file
111
src/components/dialog/content/signin/PasswordFields.vue
Normal file
@@ -0,0 +1,111 @@
|
||||
<template>
|
||||
<!-- Password Field -->
|
||||
<FormField v-slot="$field" name="password" class="flex flex-col gap-2">
|
||||
<div class="flex justify-between items-center mb-2">
|
||||
<label
|
||||
class="opacity-80 text-base font-medium"
|
||||
for="comfy-org-sign-up-password"
|
||||
>
|
||||
{{ t('auth.signup.passwordLabel') }}
|
||||
</label>
|
||||
</div>
|
||||
<Password
|
||||
v-model="password"
|
||||
input-id="comfy-org-sign-up-password"
|
||||
pt:pc-input-text:root:autocomplete="new-password"
|
||||
name="password"
|
||||
:feedback="false"
|
||||
toggle-mask
|
||||
:placeholder="t('auth.signup.passwordPlaceholder')"
|
||||
:class="{ 'p-invalid': $field.invalid }"
|
||||
fluid
|
||||
class="h-10"
|
||||
/>
|
||||
<div class="flex flex-col gap-1">
|
||||
<small v-if="$field.dirty || $field.invalid" class="text-sm">
|
||||
{{ t('validation.password.requirements') }}:
|
||||
<ul class="mt-1 space-y-1">
|
||||
<li
|
||||
:class="{
|
||||
'text-red-500': !passwordChecks.length
|
||||
}"
|
||||
>
|
||||
{{ t('validation.password.minLength') }}
|
||||
</li>
|
||||
<li
|
||||
:class="{
|
||||
'text-red-500': !passwordChecks.uppercase
|
||||
}"
|
||||
>
|
||||
{{ t('validation.password.uppercase') }}
|
||||
</li>
|
||||
<li
|
||||
:class="{
|
||||
'text-red-500': !passwordChecks.lowercase
|
||||
}"
|
||||
>
|
||||
{{ t('validation.password.lowercase') }}
|
||||
</li>
|
||||
<li
|
||||
:class="{
|
||||
'text-red-500': !passwordChecks.number
|
||||
}"
|
||||
>
|
||||
{{ t('validation.password.number') }}
|
||||
</li>
|
||||
<li
|
||||
:class="{
|
||||
'text-red-500': !passwordChecks.special
|
||||
}"
|
||||
>
|
||||
{{ t('validation.password.special') }}
|
||||
</li>
|
||||
</ul>
|
||||
</small>
|
||||
</div>
|
||||
</FormField>
|
||||
|
||||
<!-- Confirm Password Field -->
|
||||
<FormField v-slot="$field" name="confirmPassword" class="flex flex-col gap-2">
|
||||
<label
|
||||
class="opacity-80 text-base font-medium mb-2"
|
||||
for="comfy-org-sign-up-confirm-password"
|
||||
>
|
||||
{{ t('auth.login.confirmPasswordLabel') }}
|
||||
</label>
|
||||
<Password
|
||||
name="confirmPassword"
|
||||
input-id="comfy-org-sign-up-confirm-password"
|
||||
pt:pc-input-text:root:autocomplete="new-password"
|
||||
:feedback="false"
|
||||
toggle-mask
|
||||
:placeholder="t('auth.login.confirmPasswordPlaceholder')"
|
||||
:class="{ 'p-invalid': $field.invalid }"
|
||||
fluid
|
||||
class="h-10"
|
||||
/>
|
||||
<small v-if="$field.error" class="text-red-500">{{
|
||||
$field.error.message
|
||||
}}</small>
|
||||
</FormField>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { FormField } from '@primevue/forms'
|
||||
import Password from 'primevue/password'
|
||||
import { computed, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const { t } = useI18n()
|
||||
const password = ref('')
|
||||
|
||||
// TODO: Use dynamic form to better organize the password checks.
|
||||
// Ref: https://primevue.org/forms/#dynamic
|
||||
const passwordChecks = computed(() => ({
|
||||
length: password.value.length >= 8 && password.value.length <= 32,
|
||||
uppercase: /[A-Z]/.test(password.value),
|
||||
lowercase: /[a-z]/.test(password.value),
|
||||
number: /\d/.test(password.value),
|
||||
special: /[^A-Za-z0-9]/.test(password.value)
|
||||
}))
|
||||
</script>
|
||||
@@ -1,12 +1,11 @@
|
||||
<template>
|
||||
<Form
|
||||
v-slot="$form"
|
||||
class="flex flex-col gap-6"
|
||||
:resolver="zodResolver(signUpSchema)"
|
||||
@submit="onSubmit"
|
||||
>
|
||||
<!-- Email Field -->
|
||||
<div class="flex flex-col gap-2">
|
||||
<FormField v-slot="$field" name="email" class="flex flex-col gap-2">
|
||||
<label
|
||||
class="opacity-80 text-base font-medium mb-2"
|
||||
for="comfy-org-sign-up-email"
|
||||
@@ -17,116 +16,27 @@
|
||||
pt:root:id="comfy-org-sign-up-email"
|
||||
pt:root:autocomplete="email"
|
||||
class="h-10"
|
||||
name="email"
|
||||
type="text"
|
||||
:placeholder="t('auth.signup.emailPlaceholder')"
|
||||
:invalid="$form.email?.invalid"
|
||||
:invalid="$field.invalid"
|
||||
/>
|
||||
<small v-if="$form.email?.invalid" class="text-red-500">{{
|
||||
$form.email.error.message
|
||||
<small v-if="$field.error" class="text-red-500">{{
|
||||
$field.error.message
|
||||
}}</small>
|
||||
</div>
|
||||
</FormField>
|
||||
|
||||
<!-- Password Field -->
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="flex justify-between items-center mb-2">
|
||||
<label
|
||||
class="opacity-80 text-base font-medium"
|
||||
for="comfy-org-sign-up-password"
|
||||
>
|
||||
{{ t('auth.signup.passwordLabel') }}
|
||||
</label>
|
||||
</div>
|
||||
<Password
|
||||
v-model="password"
|
||||
input-id="comfy-org-sign-up-password"
|
||||
pt:pc-input-text:root:autocomplete="new-password"
|
||||
name="password"
|
||||
:feedback="false"
|
||||
toggle-mask
|
||||
:placeholder="t('auth.signup.passwordPlaceholder')"
|
||||
:class="{ 'p-invalid': $form.password?.invalid }"
|
||||
fluid
|
||||
class="h-10"
|
||||
/>
|
||||
<div class="flex flex-col gap-1">
|
||||
<small
|
||||
v-if="$form.password?.dirty || $form.password?.invalid"
|
||||
class="text-sm"
|
||||
>
|
||||
{{ t('validation.password.requirements') }}:
|
||||
<ul class="mt-1 space-y-1">
|
||||
<li
|
||||
:class="{
|
||||
'text-red-500': !passwordChecks.length
|
||||
}"
|
||||
>
|
||||
{{ t('validation.password.minLength') }}
|
||||
</li>
|
||||
<li
|
||||
:class="{
|
||||
'text-red-500': !passwordChecks.uppercase
|
||||
}"
|
||||
>
|
||||
{{ t('validation.password.uppercase') }}
|
||||
</li>
|
||||
<li
|
||||
:class="{
|
||||
'text-red-500': !passwordChecks.lowercase
|
||||
}"
|
||||
>
|
||||
{{ t('validation.password.lowercase') }}
|
||||
</li>
|
||||
<li
|
||||
:class="{
|
||||
'text-red-500': !passwordChecks.number
|
||||
}"
|
||||
>
|
||||
{{ t('validation.password.number') }}
|
||||
</li>
|
||||
<li
|
||||
:class="{
|
||||
'text-red-500': !passwordChecks.special
|
||||
}"
|
||||
>
|
||||
{{ t('validation.password.special') }}
|
||||
</li>
|
||||
</ul>
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Confirm Password Field -->
|
||||
<div class="flex flex-col gap-2">
|
||||
<label
|
||||
class="opacity-80 text-base font-medium mb-2"
|
||||
for="comfy-org-sign-up-confirm-password"
|
||||
>
|
||||
{{ t('auth.login.confirmPasswordLabel') }}
|
||||
</label>
|
||||
<Password
|
||||
name="confirmPassword"
|
||||
input-id="comfy-org-sign-up-confirm-password"
|
||||
pt:pc-input-text:root:autocomplete="new-password"
|
||||
:feedback="false"
|
||||
toggle-mask
|
||||
:placeholder="t('auth.login.confirmPasswordPlaceholder')"
|
||||
:class="{ 'p-invalid': $form.confirmPassword?.invalid }"
|
||||
fluid
|
||||
class="h-10"
|
||||
/>
|
||||
<small v-if="$form.confirmPassword?.error" class="text-red-500">{{
|
||||
$form.confirmPassword.error.message
|
||||
}}</small>
|
||||
</div>
|
||||
<PasswordFields />
|
||||
|
||||
<!-- Personal Data Consent Checkbox -->
|
||||
<div class="flex items-center gap-2">
|
||||
<FormField
|
||||
v-slot="$field"
|
||||
name="personalDataConsent"
|
||||
class="flex items-center gap-2"
|
||||
>
|
||||
<Checkbox
|
||||
input-id="comfy-org-sign-up-personal-data-consent"
|
||||
name="personalDataConsent"
|
||||
:binary="true"
|
||||
:invalid="$form.personalDataConsent?.invalid"
|
||||
:invalid="$field.invalid"
|
||||
/>
|
||||
<label
|
||||
for="comfy-org-sign-up-personal-data-consent"
|
||||
@@ -134,10 +44,10 @@
|
||||
>
|
||||
{{ t('auth.signup.personalDataConsentLabel') }}
|
||||
</label>
|
||||
</div>
|
||||
<small v-if="$form.personalDataConsent?.error" class="text-red-500 -mt-4">{{
|
||||
$form.personalDataConsent.error.message
|
||||
}}</small>
|
||||
<small v-if="$field.error" class="text-red-500 -mt-4">{{
|
||||
$field.error.message
|
||||
}}</small>
|
||||
</FormField>
|
||||
|
||||
<!-- Submit Button -->
|
||||
<Button
|
||||
@@ -149,29 +59,18 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Form, FormSubmitEvent } from '@primevue/forms'
|
||||
import { Form, FormField, FormSubmitEvent } from '@primevue/forms'
|
||||
import { zodResolver } from '@primevue/forms/resolvers/zod'
|
||||
import Button from 'primevue/button'
|
||||
import Checkbox from 'primevue/checkbox'
|
||||
import InputText from 'primevue/inputtext'
|
||||
import Password from 'primevue/password'
|
||||
import { computed, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import { type SignUpData, signUpSchema } from '@/schemas/signInSchema'
|
||||
|
||||
const { t } = useI18n()
|
||||
const password = ref('')
|
||||
import PasswordFields from './PasswordFields.vue'
|
||||
|
||||
// TODO: Use dynamic form to better organize the password checks.
|
||||
// Ref: https://primevue.org/forms/#dynamic
|
||||
const passwordChecks = computed(() => ({
|
||||
length: password.value.length >= 8 && password.value.length <= 32,
|
||||
uppercase: /[A-Z]/.test(password.value),
|
||||
lowercase: /[a-z]/.test(password.value),
|
||||
number: /\d/.test(password.value),
|
||||
special: /[^A-Za-z0-9]/.test(password.value)
|
||||
}))
|
||||
const { t } = useI18n()
|
||||
|
||||
const emit = defineEmits<{
|
||||
submit: [values: SignUpData]
|
||||
|
||||
@@ -1135,6 +1135,10 @@
|
||||
"signOut": "Log Out",
|
||||
"success": "Signed out successfully",
|
||||
"successDetail": "You have been signed out of your account."
|
||||
},
|
||||
"passwordUpdate": {
|
||||
"success": "Password Updated",
|
||||
"successDetail": "Your password has been updated successfully"
|
||||
}
|
||||
},
|
||||
"validation": {
|
||||
@@ -1175,11 +1179,8 @@
|
||||
"title": "User Settings",
|
||||
"name": "Name",
|
||||
"email": "Email",
|
||||
"provider": "Sign-in Provider",
|
||||
"notSet": "Not set",
|
||||
"provider": "Sign in method",
|
||||
"providers": {
|
||||
"google": "Google",
|
||||
"github": "GitHub"
|
||||
}
|
||||
"updatePassword": "Update Password"
|
||||
}
|
||||
}
|
||||
@@ -37,6 +37,10 @@
|
||||
"termsText": "Al hacer clic en \"Siguiente\" o \"Registrarse\", aceptas nuestros",
|
||||
"title": "Inicia sesión en tu cuenta"
|
||||
},
|
||||
"passwordUpdate": {
|
||||
"success": "Contraseña actualizada",
|
||||
"successDetail": "Tu contraseña se ha actualizado correctamente"
|
||||
},
|
||||
"signOut": {
|
||||
"signOut": "Cerrar sesión",
|
||||
"success": "Sesión cerrada correctamente",
|
||||
@@ -1151,11 +1155,8 @@
|
||||
"name": "Nombre",
|
||||
"notSet": "No establecido",
|
||||
"provider": "Método de inicio de sesión",
|
||||
"providers": {
|
||||
"github": "GitHub",
|
||||
"google": "Google"
|
||||
},
|
||||
"title": "Configuración de usuario"
|
||||
"title": "Configuración de usuario",
|
||||
"updatePassword": "Actualizar contraseña"
|
||||
},
|
||||
"validation": {
|
||||
"invalidEmail": "Dirección de correo electrónico inválida",
|
||||
|
||||
@@ -37,6 +37,10 @@
|
||||
"termsText": "En cliquant sur \"Suivant\" ou \"S'inscrire\", vous acceptez nos",
|
||||
"title": "Connectez-vous à votre compte"
|
||||
},
|
||||
"passwordUpdate": {
|
||||
"success": "Mot de passe mis à jour",
|
||||
"successDetail": "Votre mot de passe a été mis à jour avec succès"
|
||||
},
|
||||
"signOut": {
|
||||
"signOut": "Se déconnecter",
|
||||
"success": "Déconnexion réussie",
|
||||
@@ -1151,11 +1155,8 @@
|
||||
"name": "Nom",
|
||||
"notSet": "Non défini",
|
||||
"provider": "Méthode de connexion",
|
||||
"providers": {
|
||||
"github": "GitHub",
|
||||
"google": "Google"
|
||||
},
|
||||
"title": "Paramètres utilisateur"
|
||||
"title": "Paramètres utilisateur",
|
||||
"updatePassword": "Mettre à jour le mot de passe"
|
||||
},
|
||||
"validation": {
|
||||
"invalidEmail": "Adresse e-mail invalide",
|
||||
|
||||
@@ -37,6 +37,10 @@
|
||||
"termsText": "「次へ」または「サインアップ」をクリックすると、私たちの",
|
||||
"title": "アカウントにログインする"
|
||||
},
|
||||
"passwordUpdate": {
|
||||
"success": "パスワードが更新されました",
|
||||
"successDetail": "パスワードが正常に更新されました"
|
||||
},
|
||||
"signOut": {
|
||||
"signOut": "ログアウト",
|
||||
"success": "正常にサインアウトしました",
|
||||
@@ -1151,11 +1155,8 @@
|
||||
"name": "名前",
|
||||
"notSet": "未設定",
|
||||
"provider": "サインイン方法",
|
||||
"providers": {
|
||||
"github": "GitHub",
|
||||
"google": "Google"
|
||||
},
|
||||
"title": "ユーザー設定"
|
||||
"title": "ユーザー設定",
|
||||
"updatePassword": "パスワードを更新"
|
||||
},
|
||||
"validation": {
|
||||
"invalidEmail": "無効なメールアドレス",
|
||||
|
||||
@@ -37,6 +37,10 @@
|
||||
"termsText": "\"다음\" 또는 \"가입하기\"를 클릭하면 우리의",
|
||||
"title": "계정에 로그인"
|
||||
},
|
||||
"passwordUpdate": {
|
||||
"success": "비밀번호가 업데이트되었습니다",
|
||||
"successDetail": "비밀번호가 성공적으로 업데이트되었습니다"
|
||||
},
|
||||
"signOut": {
|
||||
"signOut": "로그아웃",
|
||||
"success": "성공적으로 로그아웃되었습니다",
|
||||
@@ -1151,11 +1155,8 @@
|
||||
"name": "이름",
|
||||
"notSet": "설정되지 않음",
|
||||
"provider": "로그인 방법",
|
||||
"providers": {
|
||||
"github": "GitHub",
|
||||
"google": "Google"
|
||||
},
|
||||
"title": "사용자 설정"
|
||||
"title": "사용자 설정",
|
||||
"updatePassword": "비밀번호 업데이트"
|
||||
},
|
||||
"validation": {
|
||||
"invalidEmail": "유효하지 않은 이메일 주소",
|
||||
|
||||
@@ -37,6 +37,10 @@
|
||||
"termsText": "Нажимая \"Далее\" или \"Зарегистрироваться\", вы соглашаетесь с нашими",
|
||||
"title": "Войдите в свой аккаунт"
|
||||
},
|
||||
"passwordUpdate": {
|
||||
"success": "Пароль обновлён",
|
||||
"successDetail": "Ваш пароль был успешно обновлён"
|
||||
},
|
||||
"signOut": {
|
||||
"signOut": "Выйти",
|
||||
"success": "Вы успешно вышли из системы",
|
||||
@@ -1151,11 +1155,8 @@
|
||||
"name": "Имя",
|
||||
"notSet": "Не задано",
|
||||
"provider": "Способ входа",
|
||||
"providers": {
|
||||
"github": "GitHub",
|
||||
"google": "Google"
|
||||
},
|
||||
"title": "Настройки пользователя"
|
||||
"title": "Настройки пользователя",
|
||||
"updatePassword": "Обновить пароль"
|
||||
},
|
||||
"validation": {
|
||||
"invalidEmail": "Недействительный адрес электронной почты",
|
||||
|
||||
@@ -37,6 +37,10 @@
|
||||
"termsText": "点击“下一步”或“注册”即表示您同意我们的",
|
||||
"title": "登录您的账户"
|
||||
},
|
||||
"passwordUpdate": {
|
||||
"success": "密码已更新",
|
||||
"successDetail": "您的密码已成功更新"
|
||||
},
|
||||
"signOut": {
|
||||
"signOut": "退出登录",
|
||||
"success": "成功退出登录",
|
||||
@@ -1151,11 +1155,8 @@
|
||||
"name": "名称",
|
||||
"notSet": "未设置",
|
||||
"provider": "登录方式",
|
||||
"providers": {
|
||||
"github": "GitHub",
|
||||
"google": "Google"
|
||||
},
|
||||
"title": "用户设置"
|
||||
"title": "用户设置",
|
||||
"updatePassword": "更新密码"
|
||||
},
|
||||
"validation": {
|
||||
"invalidEmail": "无效的电子邮件地址",
|
||||
|
||||
@@ -12,21 +12,34 @@ export const signInSchema = z.object({
|
||||
|
||||
export type SignInData = z.infer<typeof signInSchema>
|
||||
|
||||
export const signUpSchema = z
|
||||
.object({
|
||||
const passwordSchema = z.object({
|
||||
password: z
|
||||
.string()
|
||||
.min(8, t('validation.minLength', { length: 8 }))
|
||||
.max(32, t('validation.maxLength', { length: 32 }))
|
||||
.regex(/[A-Z]/, t('validation.password.uppercase'))
|
||||
.regex(/[a-z]/, t('validation.password.lowercase'))
|
||||
.regex(/\d/, t('validation.password.number'))
|
||||
.regex(/[^A-Za-z0-9]/, t('validation.password.special')),
|
||||
confirmPassword: z.string().min(1, t('validation.required'))
|
||||
})
|
||||
|
||||
export const updatePasswordSchema = passwordSchema.refine(
|
||||
(data) => data.password === data.confirmPassword,
|
||||
{
|
||||
message: t('validation.password.match'),
|
||||
path: ['confirmPassword']
|
||||
}
|
||||
)
|
||||
|
||||
export type UpdatePasswordData = z.infer<typeof updatePasswordSchema>
|
||||
|
||||
export const signUpSchema = passwordSchema
|
||||
.extend({
|
||||
email: z
|
||||
.string()
|
||||
.email(t('validation.invalidEmail'))
|
||||
.min(1, t('validation.required')),
|
||||
password: z
|
||||
.string()
|
||||
.min(8, t('validation.minLength', { length: 8 }))
|
||||
.max(32, t('validation.maxLength', { length: 32 }))
|
||||
.regex(/[A-Z]/, t('validation.password.uppercase'))
|
||||
.regex(/[a-z]/, t('validation.password.lowercase'))
|
||||
.regex(/\d/, t('validation.password.number'))
|
||||
.regex(/[^A-Za-z0-9]/, t('validation.password.special')),
|
||||
confirmPassword: z.string().min(1, t('validation.required')),
|
||||
personalDataConsent: z.boolean()
|
||||
})
|
||||
.refine((data) => data.password === data.confirmPassword, {
|
||||
|
||||
@@ -9,6 +9,7 @@ import PromptDialogContent from '@/components/dialog/content/PromptDialogContent
|
||||
import SettingDialogContent from '@/components/dialog/content/SettingDialogContent.vue'
|
||||
import SignInContent from '@/components/dialog/content/SignInContent.vue'
|
||||
import TopUpCreditsDialogContent from '@/components/dialog/content/TopUpCreditsDialogContent.vue'
|
||||
import UpdatePasswordContent from '@/components/dialog/content/UpdatePasswordContent.vue'
|
||||
import ManagerDialogContent from '@/components/dialog/content/manager/ManagerDialogContent.vue'
|
||||
import ManagerHeader from '@/components/dialog/content/manager/ManagerHeader.vue'
|
||||
import ManagerProgressFooter from '@/components/dialog/footer/ManagerProgressFooter.vue'
|
||||
@@ -363,6 +364,21 @@ export const useDialogService = () => {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a dialog for updating the current user's password.
|
||||
*/
|
||||
function showUpdatePasswordDialog() {
|
||||
return dialogStore.showDialog({
|
||||
key: 'global-update-password',
|
||||
component: UpdatePasswordContent,
|
||||
headerComponent: ComfyOrgHeader,
|
||||
props: {
|
||||
onSuccess: () =>
|
||||
dialogStore.closeDialog({ key: 'global-update-password' })
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
showLoadWorkflowWarning,
|
||||
showMissingModelsWarning,
|
||||
@@ -377,6 +393,7 @@ export const useDialogService = () => {
|
||||
showApiNodesSignInDialog,
|
||||
showSignInDialog,
|
||||
showTopUpCreditsDialog,
|
||||
showUpdatePasswordDialog,
|
||||
prompt,
|
||||
confirm
|
||||
}
|
||||
|
||||
@@ -118,6 +118,19 @@ export const useFirebaseAuthService = () => {
|
||||
reportError
|
||||
)
|
||||
|
||||
const updatePassword = wrapWithErrorHandlingAsync(
|
||||
async (newPassword: string) => {
|
||||
await authStore.updatePassword(newPassword)
|
||||
toastStore.add({
|
||||
severity: 'success',
|
||||
summary: t('auth.passwordUpdate.success'),
|
||||
detail: t('auth.passwordUpdate.successDetail'),
|
||||
life: 5000
|
||||
})
|
||||
},
|
||||
reportError
|
||||
)
|
||||
|
||||
return {
|
||||
logout,
|
||||
sendPasswordReset,
|
||||
@@ -127,6 +140,7 @@ export const useFirebaseAuthService = () => {
|
||||
signInWithGoogle,
|
||||
signInWithGithub,
|
||||
signInWithEmail,
|
||||
signUpWithEmail
|
||||
signUpWithEmail,
|
||||
updatePassword
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,8 @@ import {
|
||||
setPersistence,
|
||||
signInWithEmailAndPassword,
|
||||
signInWithPopup,
|
||||
signOut
|
||||
signOut,
|
||||
updatePassword
|
||||
} from 'firebase/auth'
|
||||
import { defineStore } from 'pinia'
|
||||
import { computed, ref } from 'vue'
|
||||
@@ -224,6 +225,14 @@ export const useFirebaseAuthStore = defineStore('firebaseAuth', () => {
|
||||
sendPasswordResetEmail(authInstance, email)
|
||||
)
|
||||
|
||||
/** Update password for current user */
|
||||
const _updatePassword = async (newPassword: string): Promise<void> => {
|
||||
if (!currentUser.value) {
|
||||
throw new FirebaseAuthStoreError(t('toastMessages.userNotAuthenticated'))
|
||||
}
|
||||
await updatePassword(currentUser.value, newPassword)
|
||||
}
|
||||
|
||||
const addCredits = async (
|
||||
requestBodyContent: CreditPurchasePayload
|
||||
): Promise<CreditPurchaseResponse> => {
|
||||
@@ -319,6 +328,7 @@ export const useFirebaseAuthStore = defineStore('firebaseAuth', () => {
|
||||
initiateCreditPurchase,
|
||||
fetchBalance,
|
||||
accessBillingPortal,
|
||||
sendPasswordReset
|
||||
sendPasswordReset,
|
||||
updatePassword: _updatePassword
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user