Skip to content

Commit

Permalink
Implemented password reset.
Browse files Browse the repository at this point in the history
  • Loading branch information
Utar94 committed Dec 20, 2023
1 parent 6279807 commit aa61ecb
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 6 deletions.
8 changes: 7 additions & 1 deletion src/api/account.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Actor } from "@/types/aggregate";
import type { RecoverPasswordPayload, RegisterPayload, SignInPayload } from "@/types/account";
import type { RecoverPasswordPayload, RegisterPayload, ResetPasswordPayload, SignInPayload } from "@/types/account";
import { useUserStore } from "@/stores/user";

function sleep(delay: number): Promise<void> {
Expand All @@ -23,6 +23,12 @@ export async function register(payload: RegisterPayload): Promise<void> {
users.create(payload);
}

export async function resetPassword(payload: ResetPasswordPayload): Promise<Actor | undefined> {
await sleep(2500);
const users = useUserStore();
return users.resetPassword(payload);
}

export async function signIn(payload: SignInPayload): Promise<Actor> {
await sleep(2500);
const users = useUserStore();
Expand Down
20 changes: 18 additions & 2 deletions src/stores/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { nanoid } from "nanoid";
import UserRepository from "./userRepository";
import type { Actor } from "@/types/aggregate";
import type { ErrorDetail } from "@/types/api";
import type { RegisterPayload, SignInPayload } from "@/types/account";
import type { RegisterPayload, ResetPasswordPayload, SignInPayload } from "@/types/account";
import type { User } from "@/types/users";

function buildFullName(...names: (string | undefined)[]): string | undefined {
Expand Down Expand Up @@ -100,6 +100,22 @@ export const useUserStore = defineStore("user", () => {
users.save(user, passwordHash);
}

function resetPassword(payload: ResetPasswordPayload): Actor | undefined {
// Initialize storage
const users = new UserRepository(localStorage);

// Try finding the user by username or email address
const user: User | undefined = users.findByUsername(payload.token) ?? users.findByEmail(payload.token);
if (!user) {
return;
}

// Change the user password
users.save(user, hash(payload.password));

return toActor(user);
}

function signIn(payload: SignInPayload): Actor {
// Initialize storage
const users = new UserRepository(localStorage);
Expand Down Expand Up @@ -155,5 +171,5 @@ export const useUserStore = defineStore("user", () => {
return actor;
}

return { create, signIn, verifyEmail };
return { create, resetPassword, signIn, verifyEmail };
});
5 changes: 5 additions & 0 deletions src/types/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ export type RegisterPayload = {
lastName?: string;
};

export type ResetPasswordPayload = {
token: string;
password: string;
};

export type SignInPayload = {
username: string;
password?: string;
Expand Down
3 changes: 2 additions & 1 deletion src/views/account/ConfirmView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ onMounted(async () => {
const token = route.params.token as string;
const actor: Actor | undefined = await confirm(token);
if (!actor) {
return router.push({ name: "SignIn" });
router.push({ name: "SignIn" });
return;
}
account.signIn(actor);
router.push({ name: "Profile" });
Expand Down
2 changes: 1 addition & 1 deletion src/views/account/RegisterView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import { register } from "@/api/account";
const router = useRouter();
const loading = ref<boolean>(false);
const payload = ref<RegisterPayload>({ username: "" });
const passwordConfirmation = ref<string>();
const payload = ref<RegisterPayload>({ username: "" });
const success = ref<boolean>(false);
async function submit(): Promise<void> {
Expand Down
71 changes: 70 additions & 1 deletion src/views/account/ResetPasswordView.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,76 @@
<script setup lang="ts"></script>
<script setup lang="ts">
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import { onMounted, ref } from "vue";
import { useRoute, useRouter } from "vue-router";
import type { Actor } from "@/types/aggregate";
import type { ResetPasswordPayload } from "@/types/account";
import { resetPassword } from "@/api/account";
import { useAccountStore } from "@/stores/account";
const account = useAccountStore();
const route = useRoute();
const router = useRouter();
const loading = ref<boolean>(false);
const passwordConfirmation = ref<string>();
const payload = ref<ResetPasswordPayload>({ token: "", password: "" });
async function submit(): Promise<void> {
if (!loading.value) {
loading.value = true;
try {
const actor: Actor | undefined = await resetPassword(payload.value);
if (!actor) {
router.push({ name: "SignIn" });
return;
}
account.signIn(actor);
router.push({ name: "Profile" });
} catch (e) {
console.error(e); // TODO(fpion): error handling
} finally {
loading.value = false;
}
}
}
onMounted(() => (payload.value.token = route.params.token as string));
</script>

<template>
<main class="container">
<h1>Reset Password</h1>
<form @submit.prevent="submit">
<div class="row">
<div class="col">
<div class="mb-3">
<label class="form-label" for="password">Password <span class="text-danger">*</span></label>
<input class="form-control" id="password" placeholder="Password" required type="password" v-model="payload.password" />
</div>
</div>
<div class="col">
<div class="mb-3">
<label class="form-label" for="password-confirmation"> Password Confirmation <span class="text-danger">*</span> </label>
<input
class="form-control"
id="password-confirmation"
placeholder="Password Confirmation"
required
type="password"
v-model="passwordConfirmation"
/>
</div>
</div>
</div>
<button class="btn btn-primary" :disabled="loading" type="submit">
<span v-if="loading">
<span class="spinner-border spinner-border-sm" aria-hidden="true"></span>
<span class="visually-hidden">Loading...</span>
</span>
<FontAwesomeIcon v-else :icon="['fas', 'key']" />
Reset Password
</button>
</form>
</main>
</template>

0 comments on commit aa61ecb

Please sign in to comment.