219 lines
3.8 KiB
Vue
219 lines
3.8 KiB
Vue
<script setup>
|
|
import { inject, ref } from 'vue';
|
|
|
|
import Password from '#/components/form/password.vue';
|
|
import Ellipsis from '#/components/loading/ellipsis.vue';
|
|
|
|
import { post } from '#/src/api.js';
|
|
|
|
const pageContext = inject('pageContext');
|
|
const { userId, token } = pageContext.urlParsed.search;
|
|
const allowRecovery = pageContext.env.emailEnabled;
|
|
|
|
const credential = ref('');
|
|
|
|
const submitted = ref(false);
|
|
const success = ref(false);
|
|
const errorMsg = ref(null);
|
|
const password = ref(null);
|
|
|
|
async function request() {
|
|
submitted.value = true;
|
|
errorMsg.value = null;
|
|
|
|
try {
|
|
await post('/passwords/request', {
|
|
credential: credential.value,
|
|
}, {
|
|
successFeedback: 'Password reset requested, please check your e-mail inbox',
|
|
appendErrorMessage: true,
|
|
});
|
|
|
|
success.value = true;
|
|
}
|
|
catch (error) {
|
|
errorMsg.value = error.message;
|
|
}
|
|
finally {
|
|
submitted.value = false;
|
|
}
|
|
}
|
|
|
|
async function reset() {
|
|
submitted.value = true;
|
|
errorMsg.value = null;
|
|
|
|
try {
|
|
await post('/passwords/reset', {
|
|
userId,
|
|
token,
|
|
password: password.value,
|
|
}, {
|
|
successFeedback: 'Password reset successfully',
|
|
appendErrorMessage: true,
|
|
});
|
|
|
|
success.value = true;
|
|
}
|
|
catch (error) {
|
|
errorMsg.value = error.message;
|
|
}
|
|
finally {
|
|
submitted.value = false;
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div class="recovery-container">
|
|
<h3 class="heading">
|
|
<template v-if="userId && token">Password reset</template>
|
|
<template v-else>Account recovery</template>
|
|
</h3>
|
|
|
|
<template v-if="allowRecovery">
|
|
<strong
|
|
v-if="errorMsg"
|
|
class="error"
|
|
>{{ errorMsg }}</strong>
|
|
|
|
<form
|
|
v-else-if="userId && token"
|
|
class="login-panel"
|
|
@submit.prevent="reset"
|
|
>
|
|
<div
|
|
v-if="success"
|
|
class="success"
|
|
>
|
|
<strong class="success-heading">Password reset successfully</strong>
|
|
|
|
<a
|
|
href="/login"
|
|
class="link"
|
|
>Go to login</a>
|
|
</div>
|
|
|
|
<template v-else>
|
|
<Password
|
|
:password="password"
|
|
@input="(newPassword) => password = newPassword"
|
|
/>
|
|
|
|
<Ellipsis v-if="submitted" />
|
|
|
|
<button
|
|
v-else
|
|
class="button button-submit"
|
|
>Reset password</button>
|
|
</template>
|
|
</form>
|
|
|
|
<form
|
|
v-else-if="allowRecovery"
|
|
class="login-panel"
|
|
@submit.prevent="request"
|
|
>
|
|
<div
|
|
v-if="success"
|
|
class="success"
|
|
>
|
|
<strong class="success-heading">Password reset requested</strong>
|
|
If the account exists, instructions have been sent to the associated e-mail address.
|
|
</div>
|
|
|
|
<template v-else>
|
|
<input
|
|
v-model="credential"
|
|
placeholder="Username or e-mail"
|
|
class="input"
|
|
>
|
|
|
|
<Ellipsis v-if="submitted" />
|
|
|
|
<button
|
|
v-else
|
|
class="button button-submit"
|
|
>Request password reset</button>
|
|
</template>
|
|
</form>
|
|
</template>
|
|
|
|
<span
|
|
v-else
|
|
class="error"
|
|
>Password recovery is currently unavailable</span>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.input {
|
|
width: 100%;
|
|
}
|
|
|
|
.heading {
|
|
text-align: center;
|
|
margin: 1rem;
|
|
}
|
|
|
|
.recovery-container {
|
|
display: flex;
|
|
flex-direction: column;
|
|
flex-grow: 1;
|
|
justify-content: center;
|
|
align-items: center;
|
|
background: var(--background-base-10);
|
|
}
|
|
|
|
.login-panel {
|
|
width: 20rem;
|
|
max-width: 100%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: .5rem;
|
|
padding: 1rem;
|
|
margin: 0 1rem 1rem 1rem;
|
|
border-radius: .5rem;
|
|
box-shadow: 0 0 3px var(--shadow-weak-30);
|
|
background: var(--background-base);
|
|
font-size: 1rem;
|
|
|
|
.button {
|
|
justify-content: center;
|
|
}
|
|
|
|
.link {
|
|
margin-top: .5rem;
|
|
text-align: center;
|
|
}
|
|
}
|
|
|
|
.error {
|
|
padding: 1rem;
|
|
color: var(--error);
|
|
}
|
|
|
|
.success {
|
|
justify-content: center;
|
|
line-height: 1.25;
|
|
text-align: center;
|
|
|
|
.link {
|
|
display: block;
|
|
margin-top: 1rem;
|
|
text-align: center;
|
|
font-weight: bold;
|
|
}
|
|
}
|
|
|
|
.success-heading {
|
|
display: block;
|
|
margin-bottom: .5rem;
|
|
}
|
|
|
|
.load-container {
|
|
justify-content: center;
|
|
padding: 1rem 1rem .5rem 1rem;
|
|
}
|
|
</style>
|