Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 86ea910

Browse filesBrowse files
committed
fix(login) quick fix
1 parent 6285fd2 commit 86ea910
Copy full SHA for 86ea910

File tree

Expand file treeCollapse file tree

7 files changed

+149
-21
lines changed
Filter options
Expand file treeCollapse file tree

7 files changed

+149
-21
lines changed

‎composables/useAuth.ts

Copy file name to clipboardExpand all lines: composables/useAuth.ts
+20-4Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,16 +79,32 @@ export async function registerWithEmail(
7979
}
8080
}
8181

82-
export async function loginWithEmail(usernameOrEmail: string, password: string): Promise<boolean> {
82+
export async function loginWithEmail(usernameOrEmail: string, password: string): Promise<FormValidation|undefined> {
8383

8484
try {
85-
const user = await $fetch<IUser>('/api/auth/login', { method: 'POST', body: { usernameOrEmail: usernameOrEmail, password: password } })
85+
const {data: user, error} = await useFetch<IUser>('/api/auth/login', { method: 'POST', body: { usernameOrEmail: usernameOrEmail, password: password } })
86+
87+
88+
if (error.value) {
89+
type ErrorData = {
90+
data: ErrorData
91+
}
92+
93+
const errorData = error.value as unknown as ErrorData
94+
const errors = errorData.data.data as unknown as string
95+
const res = JSON.parse(errors)
96+
const errorMap = new Map<string, { check: InputValidation; }>(Object.entries(res));
97+
98+
return { hasErrors: true, errors: errorMap }
99+
}
100+
101+
86102
console.log(user)
87103
useState('user').value = user
88104
await useRouter().push('/topics')
89-
return true
105+
return undefined
90106
} catch (e) {
91-
return false
107+
return undefined
92108
}
93109

94110
}

‎pages/login.vue

Copy file name to clipboardExpand all lines: pages/login.vue
+25-9Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,37 @@
11
<script setup lang="ts">
22
import { ref } from "@vue/reactivity";
33
import { loginWithEmail } from "~/composables/useAuth";
4-
import type {Ref} from "vue"
4+
import type { Ref } from "vue"
55
66
const usernameOrEmail = ref('')
77
const password = ref('')
8-
const hasError: Ref<boolean|null> = ref(null)
9-
const errorMessage: Ref<string|null> = ref(null)
8+
const hasError: Ref<boolean | null> = ref(null)
9+
const errorMessage: Ref<string | null> = ref(null)
10+
const errors: Ref<Map<string, { check: InputValidation; }> | undefined> = ref(new Map<string, { check: InputValidation }>())
11+
let response: Ref<FormValidation | undefined> = ref({ hasErrors: false })
1012
1113
definePageMeta({
1214
middleware: 'guest'
1315
})
1416
1517
const postLoginForm = async function () {
16-
const res = await loginWithEmail(usernameOrEmail.value, password.value)
17-
if (!res) {
18+
response.value = await loginWithEmail(usernameOrEmail.value, password.value)
19+
if (!response.value) {
1820
errorMessage.value = 'Invalid Credentials'
1921
hasError.value = true
2022
setTimeout(() => {
2123
hasError.value = false
2224
}, 3000)
2325
}
26+
27+
errors.value = response?.value?.errors
28+
29+
2430
}
2531
</script>
2632

2733
<template>
28-
<div
29-
class="h-screen bg-white dark:bg-black">
34+
<div class="h-screen bg-white dark:bg-black">
3035
<div class="flex items-center justify-center px-4 sm:px-6 lg:px-8">
3136
<div class="max-w-md w-full space-y-8">
3237
<div>
@@ -42,6 +47,17 @@ const postLoginForm = async function () {
4247
<h2 class="mt-6 py-9 text-center text-3xl font-extrabold text-gray-900 dark:text-gray-400">Sign in</h2>
4348
</div>
4449

50+
<div v-if="response?.hasErrors && errors"
51+
class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative mt-3" role="alert">
52+
<strong class="font-bold">Oops, try again! </strong>
53+
54+
<ul class="block sm:inline">
55+
<li v-for="[key, value] in errors">
56+
{{ value.check.errorMessage }}
57+
</li>
58+
</ul>
59+
</div>
60+
4561
<div v-if="hasError" class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative"
4662
role="alert">
4763
<strong class="font-bold">Oops, try again! </strong>
@@ -67,8 +83,8 @@ const postLoginForm = async function () {
6783
</div>
6884
<div>
6985
<label for="password" class="sr-only">Password</label>
70-
<input v-model="password" id="password" name="password" type="password" autocomplete="current-password"
71-
required
86+
<input v-model="password" id="password" name="password" required type="password"
87+
autocomplete="current-password"
7288
class="appearance-none rounded-none relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-b-md focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 sm:text-sm"
7389
placeholder="Password">
7490
</div>

‎pages/register.vue

Copy file name to clipboardExpand all lines: pages/register.vue
-2Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,6 @@ async function postRegisterForm() {
4141
{{ value.check.errorMessage }}
4242
</li>
4343
</ul>
44-
45-
4644
</div>
4745
<form v-on:submit.prevent class="mt-8 space-y-6" action="#" method="POST">
4846
<input type="hidden" name="remember" value="true" />

‎server/api/auth/login.ts

Copy file name to clipboardExpand all lines: server/api/auth/login.ts
+30-2Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,50 @@ import bcrypt from 'bcrypt'
33
import { getUserByEmail } from '~/server/database/repositories/userRespository';
44
import { sendError, H3Event } from "h3"
55
import { makeSession } from '~~/server/services/sessionService';
6+
import { validateLogin } from '~/server/services/userService'
67

78
export default eventHandler(async (event: H3Event) => {
89
const body = await readBody(event)
910
const usernameOrEmail: string = body.usernameOrEmail
1011
const password: string = body.password
1112

13+
const data = body
14+
15+
const validation = await validateLogin(data)
16+
17+
if (validation.hasErrors === true) {
18+
const errors = JSON.stringify(Object.fromEntries(validation.errors))
19+
return sendError(event, createError({ statusCode: 422, data: errors }))
20+
}
21+
1222
const user = await getUserByEmail(usernameOrEmail)
1323

1424
if (user === null) {
1525
sendError(event, createError({ statusCode: 401, statusMessage: 'Unauthenticated' }))
1626
}
1727

18-
const isPasswordCorrect = bcrypt.compare(password, user.password)
28+
if(user.password == undefined) {
29+
sendError(event, createError({ statusCode: 401, statusMessage: 'Unauthenticated' }))
30+
}
31+
32+
const isPasswordCorrect = await bcrypt.compare(password, user.password)
1933

2034
if (!isPasswordCorrect) {
21-
sendError(event, createError({ statusCode: 401, statusMessage: 'Unauthenticated' }))
35+
36+
const check: InputValidation = {
37+
value: '',
38+
isBlank: false,
39+
lenghtMin8: true,
40+
key: 'Authentication',
41+
hasError: false
42+
}
43+
44+
const errors = new Map<string, { check: InputValidation }>()
45+
errors.set('Authentication', { 'check': check })
46+
47+
48+
const errorsRes = JSON.stringify(Object.fromEntries(new Map()))
49+
sendError(event, createError({ statusCode: 401, data: 'Unauthenticated' }))
2250
}
2351

2452

‎server/services/userService.ts

Copy file name to clipboardExpand all lines: server/services/userService.ts
+14-1Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import { IUser } from '~~/types/IUser';
22
import { RegistationRequest } from '~~/types/IRegistration';
3-
import { validate } from '~~/server/services/validator';
3+
import { validate, validateSignIn } from '~~/server/services/validator';
44
import { H3Event } from 'h3';
55
import { getUserBySessionToken } from './sessionService';
66
import { isString } from '@vueuse/core';
77
import { User } from '@prisma/client';
8+
import { LoginRequest } from '~~/types/ILogin';
89

910
export async function validateUser(data: RegistationRequest): Promise<FormValidation> {
1011

@@ -18,6 +19,18 @@ export async function validateUser(data: RegistationRequest): Promise<FormValida
1819
return { hasErrors: false }
1920
}
2021

22+
export async function validateLogin(data: LoginRequest): Promise<FormValidation> {
23+
24+
const errors = await validateSignIn(data)
25+
26+
if (errors.size > 0) {
27+
28+
return { hasErrors: true, errors }
29+
}
30+
31+
return { hasErrors: false }
32+
}
33+
2134
export function sanitizeUserForFrontend(user: User | undefined): User | undefined {
2235

2336
if (!user) {

‎server/services/validator.ts

Copy file name to clipboardExpand all lines: server/services/validator.ts
+47-3Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import { RegistationRequest } from '~~/types/IRegistration';
22
import { getUserByEmail, getUserByUserName } from '~/server/database/repositories/userRespository';
3+
import { LoginRequest } from '~~/types/ILogin';
34

45
export async function validate(data: RegistationRequest) {
56

67
const errors = new Map<string, { check: InputValidation }>()
78

89
for (const [key, value] of Object.entries(data)) {
9-
let val = await runChecks(key, value)
10+
let val = await validateRegistration(key, value)
1011

1112
if (val.hasError) {
1213
errors.set(key, { 'check': val })
@@ -16,7 +17,22 @@ export async function validate(data: RegistationRequest) {
1617
return errors
1718
}
1819

19-
async function runChecks(key: string, value: string): Promise<InputValidation> {
20+
export async function validateSignIn(data: LoginRequest) {
21+
22+
const errors = new Map<string, { check: InputValidation }>()
23+
24+
for (const [key, value] of Object.entries(data)) {
25+
let val = await validateLogin(key, value)
26+
27+
if (val.hasError) {
28+
errors.set(key, { 'check': val })
29+
}
30+
}
31+
32+
return errors
33+
}
34+
35+
async function validateRegistration(key: string, value: string): Promise<InputValidation> {
2036
const check: InputValidation = {
2137
value,
2238
isBlank: false,
@@ -74,4 +90,32 @@ async function runChecks(key: string, value: string): Promise<InputValidation> {
7490
function validateEmail(email: string): boolean {
7591
const re = /\S+@\S+\.\S+/;
7692
return re.test(email);
77-
}
93+
}
94+
95+
async function validateLogin(key: string, value: string): Promise<InputValidation> {
96+
const check: InputValidation = {
97+
value,
98+
isBlank: false,
99+
lenghtMin8: true,
100+
key,
101+
hasError: false
102+
}
103+
104+
if (value == '' || value == null) {
105+
check.isBlank = true
106+
check.hasError = true
107+
check.errorMessage = `${key} is required`
108+
return check
109+
}
110+
111+
if (key == 'password') {
112+
if (value.length < 8) {
113+
check.hasError = true
114+
check.errorMessage = `password must be at least 8 characters`
115+
}
116+
check.lenghtMin8 = false
117+
}
118+
119+
120+
return check
121+
}

‎types/ILogin.ts

Copy file name to clipboard
+13Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
export type ILoginErrors = {
2+
hasErrors?: string
3+
}
4+
5+
export type LoginResponse = {
6+
hasErrors: boolean,
7+
errors?: ILoginErrors
8+
}
9+
10+
export type LoginRequest = {
11+
usernameOrEmail: string,
12+
password: string
13+
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.