@@ -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