@@ -21,6 +21,22 @@ export const auth_signup = (
|
||||
password: password
|
||||
});
|
||||
|
||||
export const send_recovery_key = (
|
||||
username: string
|
||||
): Promise<Responses.Success | Responses.Error> =>
|
||||
post<Requests.SendRecoveryKey>('/api/auth/send_key', {
|
||||
username: username
|
||||
});
|
||||
|
||||
export const reset_password = (
|
||||
key: string,
|
||||
password: string
|
||||
): Promise<Responses.Success | Responses.Error> =>
|
||||
post<Requests.ResetPassword>('/api/auth/reset', {
|
||||
key: key,
|
||||
password: password
|
||||
});
|
||||
|
||||
export const refresh_token = (
|
||||
token: string
|
||||
): Promise<Responses.Login | Responses.Error> =>
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
import type { Requests, Responses, UploadFile } from '@/dto';
|
||||
import {
|
||||
get_token,
|
||||
post_token,
|
||||
post_token_form,
|
||||
isErrorResponse
|
||||
} from './base';
|
||||
import { get_token, post_token, isErrorResponse } from './base';
|
||||
import axios from 'axios';
|
||||
|
||||
export const get_root = (
|
||||
|
||||
@@ -32,6 +32,15 @@ export namespace Requests {
|
||||
otp?: string;
|
||||
}
|
||||
|
||||
export interface SendRecoveryKey extends Base {
|
||||
username: string;
|
||||
}
|
||||
|
||||
export interface ResetPassword extends Base {
|
||||
key: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
export interface TfaSetup extends Base {
|
||||
mail: boolean;
|
||||
}
|
||||
|
||||
119
frontend/src/views/ForgotPasswordView.vue
Normal file
119
frontend/src/views/ForgotPasswordView.vue
Normal file
@@ -0,0 +1,119 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { Auth, isErrorResponse } from '@/api';
|
||||
import { useMessage, NInput, NGrid, NGi, NButton, NCard } from 'naive-ui';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { loadingMsgWrapper } from '@/utils';
|
||||
|
||||
const router = useRouter();
|
||||
const message = useMessage();
|
||||
|
||||
const email = ref('');
|
||||
const code = ref('');
|
||||
const password = ref('');
|
||||
const password2 = ref('');
|
||||
const enterCode = ref(false);
|
||||
|
||||
const reset = loadingMsgWrapper(message, async () => {
|
||||
if (password.value !== password2.value) {
|
||||
message.error("Passwords don't match");
|
||||
return;
|
||||
}
|
||||
const res = await Auth.reset_password(code.value, password.value);
|
||||
if (isErrorResponse(res)) {
|
||||
message.error(`Failed to reset password: ${res.message}`);
|
||||
} else {
|
||||
message.success('Password reset', {
|
||||
duration: 2000
|
||||
});
|
||||
login();
|
||||
}
|
||||
});
|
||||
|
||||
const sendKey = loadingMsgWrapper(message, async () => {
|
||||
const res = await Auth.send_recovery_key(email.value);
|
||||
if (isErrorResponse(res)) {
|
||||
message.error(`Failed to send recovery key: ${res.message}`);
|
||||
} else {
|
||||
message.success('If the email is registered a mail has been sent', {
|
||||
duration: 10000
|
||||
});
|
||||
enterCode.value = true;
|
||||
}
|
||||
});
|
||||
|
||||
function login() {
|
||||
router.replace('login');
|
||||
}
|
||||
|
||||
function onKey(event: KeyboardEvent) {
|
||||
if (event.key == 'Enter') enterCode.value ? reset() : sendKey();
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n-card>
|
||||
<n-grid cols="2" x-gap="16" y-gap="16">
|
||||
<template v-if="enterCode">
|
||||
<n-gi span="2">
|
||||
<n-input
|
||||
:key="1"
|
||||
type="text"
|
||||
placeholder="Recovery key"
|
||||
v-model:value="code"
|
||||
maxlength="10"
|
||||
autofocus
|
||||
@keyup="onKey"
|
||||
/>
|
||||
</n-gi>
|
||||
<n-gi span="2">
|
||||
<n-input
|
||||
type="password"
|
||||
placeholder="Password"
|
||||
v-model:value="password"
|
||||
@keyup="onKey"
|
||||
/>
|
||||
</n-gi>
|
||||
<n-gi span="2">
|
||||
<n-input
|
||||
type="password"
|
||||
placeholder="Repeat password"
|
||||
v-model:value="password2"
|
||||
@keyup="onKey"
|
||||
/>
|
||||
</n-gi>
|
||||
<n-gi>
|
||||
<n-button type="info" @click="reset">
|
||||
Reset password
|
||||
</n-button>
|
||||
</n-gi>
|
||||
<n-gi style="text-align: right">
|
||||
<n-button ghost @click="login">Go to login</n-button>
|
||||
</n-gi>
|
||||
</template>
|
||||
<template v-else>
|
||||
<n-gi span="2">
|
||||
<n-input
|
||||
:key="2"
|
||||
type="text"
|
||||
placeholder="Email"
|
||||
v-model:value="email"
|
||||
autofocus
|
||||
:input-props="{ type: 'email' }"
|
||||
@keyup="onKey"
|
||||
/>
|
||||
</n-gi>
|
||||
<n-gi>
|
||||
<n-button type="info" @click="sendKey">
|
||||
Send recovery key
|
||||
</n-button>
|
||||
</n-gi>
|
||||
<n-gi style="text-align: right">
|
||||
<n-button ghost @click="login">Go to login</n-button>
|
||||
</n-gi>
|
||||
</template>
|
||||
</n-grid>
|
||||
</n-card>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
Reference in New Issue
Block a user