support reset password by email reset link

This commit is contained in:
MaysWind
2023-08-26 23:37:02 +08:00
parent c66bc62c41
commit f31ef1649f
42 changed files with 1298 additions and 30 deletions
+4
View File
@@ -1,3 +1,5 @@
const defaultTimeout = 10000; // 10s
const requestForgetPasswordTimeout = 30000; // 30s
const baseApiUrlPath = '/api';
const baseQrcodePath = '/qrcode';
const baseProxyUrlPath = '/proxy';
@@ -7,6 +9,8 @@ const baiduMapJavascriptUrl = 'https://api.map.baidu.com/api?v=3.0';
const amapJavascriptUrl = 'https://webapi.amap.com/maps?v=2.0';
export default {
defaultTimeout: defaultTimeout,
requestForgetPasswordTimeout: requestForgetPasswordTimeout,
baseApiUrlPath: baseApiUrlPath,
baseQrcodePath: baseQrcodePath,
baseProxyUrlPath: baseProxyUrlPath,
+4
View File
@@ -33,6 +33,10 @@ export function isUserRegistrationEnabled() {
return getServerSetting('r') === '1';
}
export function isUserForgetPasswordEnabled() {
return getServerSetting('f') === '1';
}
export function isDataExportingEnabled() {
return getServerSetting('e') === '1';
}
+16 -1
View File
@@ -13,7 +13,7 @@ let needBlockRequest = false;
let blockedRequests = [];
axios.defaults.baseURL = api.baseApiUrlPath;
axios.defaults.timeout = 10000; // 10s
axios.defaults.timeout = api.defaultTimeout;
axios.interceptors.request.use(config => {
const token = userState.getToken();
@@ -102,6 +102,21 @@ export default {
firstDayOfWeek
});
},
requestResetPassword: ({ email }) => {
return axios.post('forget_password/request.json', {
email
}, {
timeout: api.requestForgetPasswordTimeout
});
},
resetPassword: ({ email, token, password }) => {
return axios.post('forget_password/reset/by_token.json?token=' + token, {
email,
password
}, {
ignoreError: true
});
},
logout: () => {
return axios.get('logout.json');
},
+16
View File
@@ -558,6 +558,7 @@ export default {
'api not found': 'Failed to request api',
'not implemented': 'Not implemented',
'database operation failed': 'Database operation failed',
'smtp server is not enabled': 'Smtp server is not enabled',
'incomplete or incorrect submission': 'Incomplete or incorrect submission',
'operation failed': 'Operation failed',
'nothing will be updated': 'Nothing will be updated',
@@ -580,6 +581,9 @@ export default {
'login name or password is invalid': 'Login name or password is invalid',
'login name or password is wrong': 'Login name or password is wrong',
'user is disabled': 'User is disabled',
'email is invalid': 'Email is invalid',
'email is empty or invalid': 'Email is empty or invalid',
'new password equals old password': 'New password equals old password',
'unauthorized access': 'Unauthorized access',
'current token is invalid': 'Current token is invalid',
'current token is expired': 'Current token is expired',
@@ -592,6 +596,7 @@ export default {
'token is not found': 'Token is not found',
'token is expired': 'Token is expired',
'token is empty': 'Token is empty',
'password reset token is invalid or expired': 'Password reset token is invalid or expired',
'passcode is invalid': 'Passcode is invalid',
'two factor backup code is invalid': 'Two factor backup code is invalid',
'two factor is not enabled': 'Two factor is not enabled',
@@ -812,7 +817,9 @@ export default {
'This year or later': 'This year or later',
'Log In': 'Log In',
'Click here to log in': 'Click here to log in',
'Back to log in': 'Back to log in',
'Don\'t have an account?': 'Don\'t have an account?',
'Forget Password?': 'Forget Password?',
'Create an account': 'Create an account',
'Username cannot be empty': 'Username cannot be empty',
'Password cannot be empty': 'Password cannot be empty',
@@ -839,6 +846,15 @@ export default {
'Use a passcode': 'Use a passcode',
'PIN code is invalid': 'PIN code is invalid',
'PIN code is wrong': 'PIN code is wrong',
'Send Reset Link': 'Send Reset Link',
'Please input your email address used for registration and we\'ll send you an email with reset password link': 'Please input your email address used for registration and we\'ll send you an email with reset password link',
'Password reset email has been sent': 'Password reset email has been sent',
'Unable to send password reset email': 'Unable to send password reset email',
'Reset Password': 'Reset Password',
'Update Password': 'Update Password',
'Please input your email address again, and input the new password.': 'Please input your email address again, and input the new password.',
'Password has been updated': 'Password has been updated',
'Unable to reset password': 'Unable to reset password',
'Sign Up': 'Sign Up',
'Overview': 'Overview',
'Asset Summary': 'Asset Summary',
+16
View File
@@ -558,6 +558,7 @@ export default {
'api not found': '接口调用失败',
'not implemented': '未实现',
'database operation failed': '数据库操作失败',
'smtp server is not enabled': 'Smtp 服务器没有启用',
'incomplete or incorrect submission': '提交不完整或不正确',
'operation failed': '操作失败',
'nothing will be updated': '没有内容更新',
@@ -580,6 +581,9 @@ export default {
'login name or password is invalid': '登录名或密码无效',
'login name or password is wrong': '登录名或密码错误',
'user is disabled': '用户已禁用',
'email is invalid': '邮箱无效',
'email is empty or invalid': '邮箱为空或无效',
'new password equals old password': '新密码与旧密码相同',
'unauthorized access': '未授权的登录',
'current token is invalid': '当前认证令牌无效',
'current token is expired': '当前认证令牌已过期',
@@ -592,6 +596,7 @@ export default {
'token is not found': '认证令牌不存在',
'token is expired': '认证令牌已过期',
'token is empty': '认证令牌为空',
'password reset token is invalid or expired': '密码重置令牌无效或已过期',
'passcode is invalid': '验证码无效',
'two factor backup code is invalid': '两步验证备用码无效',
'two factor is not enabled': '两步验证没有启用',
@@ -812,7 +817,9 @@ export default {
'This year or later': '今年或更晚',
'Log In': '登录',
'Click here to log in': '点击这里登录',
'Back to log in': '返回登录页',
'Don\'t have an account?': '还没有账号?',
'Forget Password?': '忘记密码?',
'Create an account': '创建新账号',
'Username cannot be empty': '用户名不能为空',
'Password cannot be empty': '密码不能为空',
@@ -839,6 +846,15 @@ export default {
'Use a passcode': '使用验证码',
'PIN code is invalid': 'PIN码无效',
'PIN code is wrong': 'PIN码错误',
'Send Reset Link': '发送重置链接',
'Please input your email address used for registration and we\'ll send you an email with reset password link': '请输入您注册时使用的电子邮箱地址,我们将发送一封包含重置密码链接的邮件给您',
'Password reset email has been sent': '重置密码邮件已发送',
'Unable to send password reset email': '无法发送重置密码邮件',
'Reset Password': '重置密码',
'Update Password': '更新密码',
'Please input your email address again, and input the new password.': '请再次输入您的邮箱,然后输入新的密码。',
'Password has been updated': '密码已经更新',
'Unable to reset password': '无法重置密码',
'Sign Up': '注册',
'Overview': '总览',
'Asset Summary': '资产概要',
+14
View File
@@ -5,6 +5,8 @@ import userState from '@/lib/userstate.js';
import MainLayout from '@/views/desktop/MainLayout.vue';
import LoginPage from '@/views/desktop/LoginPage.vue';
import SignUpPage from '@/views/desktop/SignupPage.vue';
import ForgetPasswordPage from '@/views/desktop/ForgetPasswordPage.vue';
import ResetPasswordPage from '@/views/desktop/ResetPasswordPage.vue';
import UnlockPage from '@/views/desktop/UnlockPage.vue';
import HomePage from '@/views/desktop/HomePage.vue';
@@ -157,6 +159,18 @@ const router = createRouter({
component: SignUpPage,
beforeEnter: checkNotLogin
},
{
path: '/forgetpassword',
component: ForgetPasswordPage,
beforeEnter: checkNotLogin
},
{
path: '/resetpassword',
component: ResetPasswordPage,
props: route => ({
token: route.query.token
})
},
{
path: '/unlock',
component: UnlockPage,
+54
View File
@@ -247,6 +247,60 @@ export const useRootStore = defineStore('root', {
userState.clearWebAuthnConfig();
this.resetAllStates(true);
},
requestResetPassword({ email }) {
return new Promise((resolve, reject) => {
services.requestResetPassword({
email
}).then(response => {
const data = response.data;
if (!data || !data.success || !data.result) {
reject({ message: 'Unable to send password reset email' });
return;
}
resolve(data.result);
}).catch(error => {
logger.error('failed to send password reset email', error);
if (error && error.processed) {
reject(error);
} else if (error.response && error.response.data && error.response.data.errorMessage) {
reject({ error: error.response.data });
} else {
reject({ message: 'Unable to send password reset email' });
}
});
});
},
resetPassword({ email, token, password }) {
return new Promise((resolve, reject) => {
services.resetPassword({
token,
email,
password
}).then(response => {
const data = response.data;
if (!data || !data.success || !data.result) {
reject({ message: 'Unable to reset password' });
return;
}
resolve(data.result);
}).catch(error => {
logger.error('failed to reset password', error);
if (error && error.processed) {
reject(error);
} else if (error.response && error.response.data && error.response.data.errorMessage) {
reject({ error: error.response.data });
} else {
reject({ message: 'Unable to reset password' });
}
});
});
},
updateUserProfile({ profile, currentPassword }) {
return new Promise((resolve, reject) => {
services.updateProfile({
+187
View File
@@ -0,0 +1,187 @@
<template>
<div class="layout-wrapper">
<router-link to="/">
<div class="auth-logo d-flex align-start gap-x-3">
<img alt="logo" class="login-page-logo" :src="ezBookkeepingLogoPath" />
<h1 class="font-weight-medium leading-normal text-2xl">{{ $t('global.app.title') }}</h1>
</div>
</router-link>
<v-row no-gutters class="auth-wrapper">
<v-col cols="12" md="8" class="d-none d-md-flex align-center justify-center position-relative">
<div class="d-flex auth-img-footer" v-if="!isDarkMode">
<v-img src="img/desktop/background.svg"/>
</div>
<div class="d-flex auth-img-footer" v-if="isDarkMode">
<v-img src="img/desktop/background-dark.svg"/>
</div>
<div class="d-flex align-center justify-center w-100 pt-10">
<v-img max-width="600px" src="img/desktop/people4.svg"/>
</div>
</v-col>
<v-col cols="12" md="4" class="auth-card d-flex flex-column">
<div class="d-flex align-center justify-center h-100">
<v-card variant="flat" class="w-100 mt-0 px-4 pt-12" max-width="500">
<v-card-text>
<h5 class="text-h5 mb-3">{{ $t('Forget Password?') }}</h5>
<p class="mb-0">{{ $t('Please input your email address used for registration and we\'ll send you an email with reset password link') }}</p>
</v-card-text>
<v-card-text class="pb-0 mb-6">
<v-form>
<v-row>
<v-col cols="12">
<v-text-field
type="email"
autocomplete="email"
autofocus="autofocus"
clearable
:disabled="requesting"
:label="$t('E-mail')"
:placeholder="$t('Your email address')"
v-model="email"
@keyup.enter="requestResetPassword"
/>
</v-col>
<v-col cols="12">
<v-btn block type="submit" :disabled="!email || requesting" @click="requestResetPassword">
{{ $t('Send Reset Link') }}
<v-progress-circular indeterminate size="24" class="ml-2" v-if="requesting"></v-progress-circular>
</v-btn>
</v-col>
<v-col cols="12">
<router-link class="d-flex align-center justify-center" to="/login"
:class="{ 'disabled': requesting }">
<v-icon :icon="icons.left"/>
<span>{{ $t('Back to log in') }}</span>
</router-link>
</v-col>
</v-row>
</v-form>
</v-card-text>
</v-card>
</div>
<v-spacer/>
<div class="d-flex align-center justify-center">
<v-card variant="flat" class="w-100 px-4 pb-4" max-width="500">
<v-card-text class="pt-0">
<v-row>
<v-col cols="12" class="text-center">
<v-menu location="bottom">
<template #activator="{ props }">
<v-btn variant="text"
:disabled="requesting"
v-bind="props">{{ currentLanguageName }}</v-btn>
</template>
<v-list>
<v-list-item v-for="(lang, locale) in allLanguages" :key="locale">
<v-list-item-title
class="cursor-pointer"
@click="changeLanguage(locale)">
{{ lang.displayName }}
</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
</v-col>
<v-col cols="12" class="d-flex align-center pt-0">
<v-divider />
</v-col>
<v-col cols="12" class="text-center text-sm">
<span>Powered by </span>
<a href="https://github.com/mayswind/ezbookkeeping" target="_blank">ezBookkeeping</a>&nbsp;<span>{{ version }}</span>
</v-col>
</v-row>
</v-card-text>
</v-card>
</div>
</v-col>
</v-row>
<confirm-dialog ref="confirmDialog"/>
<snack-bar ref="snackbar" />
</div>
</template>
<script>
import { useTheme } from 'vuetify';
import { mapStores } from 'pinia';
import { useRootStore } from '@/stores/index.js';
import { useSettingsStore } from '@/stores/setting.js';
import assetConstants from '@/consts/asset.js';
import {
mdiChevronLeft,
} from '@mdi/js';
export default {
data() {
return {
email: '',
requesting: false,
icons: {
left: mdiChevronLeft
}
};
},
computed: {
...mapStores(useRootStore, useSettingsStore),
ezBookkeepingLogoPath() {
return assetConstants.ezBookkeepingLogoPath;
},
version() {
return 'v' + this.$version;
},
allLanguages() {
return this.$locale.getAllLanguageInfos();
},
isDarkMode() {
return this.globalTheme.global.name.value === 'dark';
},
currentLanguageName() {
return this.$locale.getCurrentLanguageDisplayName();
}
},
setup() {
const theme = useTheme();
return {
globalTheme: theme
};
},
methods: {
requestResetPassword() {
const self = this;
if (!self.email) {
self.$refs.snackbar.showMessage('Email address cannot be empty');
return;
}
self.requesting = true;
self.rootStore.requestResetPassword({
email: self.email
}).then(() => {
self.requesting = false;
self.$refs.snackbar.showMessage('Password reset email has been sent');
}).catch(error => {
self.requesting = false;
if (!error.processed) {
self.$refs.snackbar.showError(error);
}
});
},
changeLanguage(locale) {
const localeDefaultSettings = this.$locale.setLanguage(locale);
this.settingsStore.updateLocalizedDefaultSettings(localeDefaultSettings);
}
}
}
</script>
+9 -1
View File
@@ -95,6 +95,11 @@
<a href="javascript:void(0);" @click="showMobileQrCode = true">
<span class="nav-item-title">{{ $t('Use on Mobile Device') }}</span>
</a>
<v-spacer/>
<router-link class="text-primary" to="/forgetpassword"
:class="{'disabled': !isUserForgetPasswordEnabled}">
{{ $t('Forget Password?') }}
</router-link>
</div>
</v-col>
@@ -176,7 +181,7 @@ import { useSettingsStore } from '@/stores/setting.js';
import { useExchangeRatesStore } from '@/stores/exchangeRates.js';
import assetConstants from '@/consts/asset.js';
import { isUserRegistrationEnabled } from '@/lib/server_settings.js';
import { isUserRegistrationEnabled, isUserForgetPasswordEnabled } from '@/lib/server_settings.js';
import {
mdiEyeOutline,
@@ -221,6 +226,9 @@ export default {
isUserRegistrationEnabled() {
return isUserRegistrationEnabled();
},
isUserForgetPasswordEnabled() {
return isUserForgetPasswordEnabled();
},
inputIsEmpty() {
return !this.username || !this.password;
},
+248
View File
@@ -0,0 +1,248 @@
<template>
<div class="layout-wrapper">
<router-link to="/">
<div class="auth-logo d-flex align-start gap-x-3">
<img alt="logo" class="login-page-logo" :src="ezBookkeepingLogoPath" />
<h1 class="font-weight-medium leading-normal text-2xl">{{ $t('global.app.title') }}</h1>
</div>
</router-link>
<v-row no-gutters class="auth-wrapper">
<v-col cols="12" md="8" class="d-none d-md-flex align-center justify-center position-relative">
<div class="d-flex auth-img-footer" v-if="!isDarkMode">
<v-img src="img/desktop/background.svg"/>
</div>
<div class="d-flex auth-img-footer" v-if="isDarkMode">
<v-img src="img/desktop/background-dark.svg"/>
</div>
<div class="d-flex align-center justify-center w-100 pt-10">
<v-img max-width="600px" src="img/desktop/people4.svg"/>
</div>
</v-col>
<v-col cols="12" md="4" class="auth-card d-flex flex-column">
<div class="d-flex align-center justify-center h-100">
<v-card variant="flat" class="w-100 mt-0 px-4 pt-12" max-width="500">
<v-card-text>
<h5 class="text-h5 mb-3">{{ $t('Reset Password') }}</h5>
<p class="mb-0">{{ $t('Please input your email address again, and input the new password.') }}</p>
</v-card-text>
<v-card-text class="pb-0 mb-6">
<v-form>
<v-row>
<v-col cols="12">
<v-text-field
type="email"
autocomplete="email"
autofocus="autofocus"
clearable
:disabled="updating"
:label="$t('E-mail')"
:placeholder="$t('Your email address')"
v-model="email"
@keyup.enter="$refs.passwordInput.focus()"
/>
</v-col>
<v-col cols="12">
<v-text-field
autocomplete="new-password"
clearable
ref="passwordInput"
:type="isNewPasswordVisible ? 'text' : 'password'"
:disabled="updating"
:label="$t('Password')"
:placeholder="$t('Your password')"
:append-inner-icon="isNewPasswordVisible ? icons.eyeSlash : icons.eye"
v-model="newPassword"
@click:append-inner="isNewPasswordVisible = !isNewPasswordVisible"
@keyup.enter="$refs.confirmPasswordInput.focus()"
/>
</v-col>
<v-col cols="12">
<v-text-field
clearable
ref="confirmPasswordInput"
:type="isConfirmPasswordVisible ? 'text' : 'password'"
:disabled="updating"
:label="$t('Confirmation Password')"
:placeholder="$t('Re-enter the password')"
:append-inner-icon="isConfirmPasswordVisible ? icons.eyeSlash : icons.eye"
v-model="confirmPassword"
@click:append-inner="isConfirmPasswordVisible = !isConfirmPasswordVisible"
@keyup.enter="resetPassword"
/>
</v-col>
<v-col cols="12">
<v-btn block :disabled="!email || !newPassword || !confirmPassword || updating" @click="resetPassword">
{{ $t('Update Password') }}
<v-progress-circular indeterminate size="24" class="ml-2" v-if="updating"></v-progress-circular>
</v-btn>
</v-col>
<v-col cols="12">
<router-link class="d-flex align-center justify-center" to="/login"
:class="{ 'disabled': updating }">
<v-icon :icon="icons.left"/>
<span>{{ $t('Back to log in') }}</span>
</router-link>
</v-col>
</v-row>
</v-form>
</v-card-text>
</v-card>
</div>
<v-spacer/>
<div class="d-flex align-center justify-center">
<v-card variant="flat" class="w-100 px-4 pb-4" max-width="500">
<v-card-text class="pt-0">
<v-row>
<v-col cols="12" class="text-center">
<v-menu location="bottom">
<template #activator="{ props }">
<v-btn variant="text"
:disabled="updating"
v-bind="props">{{ currentLanguageName }}</v-btn>
</template>
<v-list>
<v-list-item v-for="(lang, locale) in allLanguages" :key="locale">
<v-list-item-title
class="cursor-pointer"
@click="changeLanguage(locale)">
{{ lang.displayName }}
</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
</v-col>
<v-col cols="12" class="d-flex align-center pt-0">
<v-divider />
</v-col>
<v-col cols="12" class="text-center text-sm">
<span>Powered by </span>
<a href="https://github.com/mayswind/ezbookkeeping" target="_blank">ezBookkeeping</a>&nbsp;<span>{{ version }}</span>
</v-col>
</v-row>
</v-card-text>
</v-card>
</div>
</v-col>
</v-row>
<confirm-dialog ref="confirmDialog"/>
<snack-bar ref="snackbar" />
</div>
</template>
<script>
import { useTheme } from 'vuetify';
import { mapStores } from 'pinia';
import { useRootStore } from '@/stores/index.js';
import { useSettingsStore } from '@/stores/setting.js';
import assetConstants from '@/consts/asset.js';
import {
mdiChevronLeft,
mdiEyeOffOutline,
mdiEyeOutline
} from '@mdi/js';
export default {
props: [
'token'
],
data() {
return {
email: '',
newPassword: '',
confirmPassword: '',
isNewPasswordVisible: false,
isConfirmPasswordVisible: false,
updating: false,
icons: {
left: mdiChevronLeft,
eye: mdiEyeOutline,
eyeSlash: mdiEyeOffOutline
}
};
},
computed: {
...mapStores(useRootStore, useSettingsStore),
inputProblemMessage() {
if (!this.email) {
return 'Email address cannot be empty';
} else if (!this.newPassword && !this.confirmPassword) {
return 'Nothing has been modified';
} else if (!this.newPassword && this.confirmPassword) {
return 'New password cannot be empty';
} else if (this.newPassword && !this.confirmPassword) {
return 'Confirmation password cannot be empty';
} else if (this.newPassword && this.confirmPassword && this.newPassword !== this.confirmPassword) {
return 'Password and confirmation password do not match';
} else {
return null;
}
},
ezBookkeepingLogoPath() {
return assetConstants.ezBookkeepingLogoPath;
},
version() {
return 'v' + this.$version;
},
allLanguages() {
return this.$locale.getAllLanguageInfos();
},
isDarkMode() {
return this.globalTheme.global.name.value === 'dark';
},
currentLanguageName() {
return this.$locale.getCurrentLanguageDisplayName();
}
},
setup() {
const theme = useTheme();
return {
globalTheme: theme
};
},
methods: {
resetPassword() {
const self = this;
const problemMessage = self.inputProblemMessage;
if (problemMessage) {
self.$refs.snackbar.showMessage(problemMessage);
return;
}
self.updating = true;
self.rootStore.resetPassword({
token: self.token,
email: self.email,
password: self.newPassword
}).then(() => {
self.updating = false;
self.$refs.snackbar.showMessage('Password has been updated');
}).catch(error => {
self.updating = false;
if (!error.processed) {
self.$refs.snackbar.showError(error);
}
});
},
changeLanguage(locale) {
const localeDefaultSettings = this.$locale.setLanguage(locale);
this.settingsStore.updateLocalizedDefaultSettings(localeDefaultSettings);
}
}
}
</script>
+68 -1
View File
@@ -35,6 +35,9 @@
</small>
</template>
<template #after>
<small>
<f7-link :class="{'disabled': !isUserForgetPasswordEnabled}" @click="showForgetPasswordSheet = true">{{ $t('Forget Password?') }}</f7-link>
</small>
</template>
</f7-list-item>
</f7-list>
@@ -127,6 +130,37 @@
</div>
</f7-page-content>
</f7-sheet>
<f7-sheet
style="height:auto"
:opened="showForgetPasswordSheet" @sheet:closed="showForgetPasswordSheet = false"
>
<f7-page-content>
<div class="display-flex padding justify-content-space-between align-items-center">
<div class="ebk-sheet-title"><b>{{ $t('Forget Password?') }}</b></div>
</div>
<div class="padding-horizontal padding-bottom">
<p class="no-margin">
<span>{{ $t('Please input your email address used for registration and we\'ll send you an email with reset password link') }}</span>
</p>
<f7-list strong class="no-margin">
<f7-list-input
type="email"
autocomplete="email"
outline
floating-label
clear-button
class="no-margin no-padding-bottom"
:label="$t('E-mail')"
:placeholder="$t('Your email address')"
v-model:value="forgetPasswordEmail"
@keyup.enter="requestResetPassword"
></f7-list-input>
</f7-list>
<f7-button large fill :class="{ 'disabled': !forgetPasswordEmail || requestingForgetPassword }" :text="$t('Send Reset Link')" @click="requestResetPassword"></f7-button>
</div>
</f7-page-content>
</f7-sheet>
</f7-page>
</template>
@@ -137,7 +171,7 @@ import { useSettingsStore } from '@/stores/setting.js';
import { useExchangeRatesStore } from '@/stores/exchangeRates.js';
import assetConstants from '@/consts/asset.js';
import { isUserRegistrationEnabled } from '@/lib/server_settings.js';
import { isUserRegistrationEnabled, isUserForgetPasswordEnabled } from '@/lib/server_settings.js';
import { getDesktopVersionPath } from '@/lib/version.js';
import { isModalShowing } from '@/lib/ui.mobile.js';
@@ -152,9 +186,12 @@ export default {
passcode: '',
backupCode: '',
tempToken: '',
forgetPasswordEmail: '',
logining: false,
verifying: false,
requestingForgetPassword: false,
show2faSheet: false,
showForgetPasswordSheet: false,
twoFAVerifyType: 'passcode'
};
},
@@ -175,6 +212,9 @@ export default {
isUserRegistrationEnabled() {
return isUserRegistrationEnabled();
},
isUserForgetPasswordEnabled() {
return isUserForgetPasswordEnabled();
},
inputIsEmpty() {
return !this.username || !this.password;
},
@@ -308,6 +348,33 @@ export default {
}
});
},
requestResetPassword() {
const self = this;
if (!self.forgetPasswordEmail) {
self.$alert('Email address cannot be empty');
return;
}
self.requestingForgetPassword = true;
self.$showLoading(() => self.requestingForgetPassword);
self.rootStore.requestResetPassword({
email: self.forgetPasswordEmail
}).then(() => {
self.requestingForgetPassword = false;
self.$hideLoading();
self.$toast('Password reset email has been sent');
}).catch(error => {
self.requestingForgetPassword = false;
self.$hideLoading();
if (!error.processed) {
self.$toast(error.message || error);
}
});
},
switch2FAVerifyType() {
if (this.twoFAVerifyType === 'passcode') {
this.twoFAVerifyType = 'backupcode';