-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
206 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
'use server' | ||
import { cookies } from 'next/headers' | ||
import { redirect } from 'next/navigation' | ||
import type { SignInSchema } from '@/auth/login/schema' | ||
import { createClient } from '@/auth/supabase/server' | ||
|
||
export const signIn = async ({ | ||
email, | ||
password | ||
}: SignInSchema): Promise<void> => { | ||
const supabase = createClient(cookies()) | ||
|
||
const { error } = await supabase.auth.signInWithPassword({ | ||
email, | ||
password | ||
}) | ||
if (error) { | ||
throw error | ||
} else { | ||
return redirect('/') | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,85 @@ | ||
import { Heading } from '@chakra-ui/react' | ||
'use client' | ||
import { useState } from 'react' | ||
import { useForm } from 'react-hook-form' | ||
import { | ||
Box, | ||
Flex, | ||
FormLabel, | ||
FormErrorMessage, | ||
FormControl, | ||
Input, | ||
Heading, | ||
useToast, | ||
useBoolean | ||
} from '@chakra-ui/react' | ||
import { signIn } from '@/auth/login/action' | ||
import { signInResolver, SignInSchema } from '@/auth/login/schema' | ||
import { PrimaryButton } from '@/components/button' | ||
|
||
export default function Login() { | ||
return <Heading>Login Page</Heading> | ||
export default function SignIn() { | ||
const toast = useToast() | ||
const [isLoading, setIsLoading] = useBoolean() | ||
const [toastId, setToastId] = useState<ReturnType<typeof toast> | undefined>( | ||
undefined | ||
) | ||
|
||
const { | ||
register, | ||
handleSubmit, | ||
formState: { errors } | ||
} = useForm<SignInSchema>({ | ||
resolver: signInResolver | ||
}) | ||
|
||
const signInHandler = handleSubmit(async (data: SignInSchema) => { | ||
try { | ||
setIsLoading.on() | ||
await signIn(data) | ||
} catch (error) { | ||
if (!toastId) { | ||
const res = toast({ | ||
title: "We're sorry, but you failed to sign in.", | ||
description: error instanceof Error ? error.message : 'unknown error', | ||
status: 'error', | ||
duration: 5000, | ||
isClosable: true, | ||
position: 'top', | ||
onCloseComplete() { | ||
setToastId(undefined) | ||
} | ||
}) | ||
setToastId(res) | ||
} | ||
} finally { | ||
setIsLoading.off() | ||
} | ||
}) | ||
|
||
return ( | ||
<> | ||
<Heading>Welcome Back!</Heading> | ||
<Heading>Sign In</Heading> | ||
<Box as="form" onSubmit={signInHandler}> | ||
<Flex flexDirection={'column'}> | ||
<FormControl isInvalid={!!errors.email}> | ||
<FormLabel>Email address</FormLabel> | ||
<Input {...register('email')} /> | ||
{errors.email && ( | ||
<FormErrorMessage>{errors.email.message}</FormErrorMessage> | ||
)} | ||
</FormControl> | ||
<FormControl isInvalid={!!errors.password}> | ||
<FormLabel>Password</FormLabel> | ||
<Input type="password" {...register('password')} /> | ||
{errors.password && ( | ||
<FormErrorMessage>{errors.password.message}</FormErrorMessage> | ||
)} | ||
</FormControl> | ||
<PrimaryButton isLoading={isLoading} type={'submit'}> | ||
Sign In | ||
</PrimaryButton> | ||
</Flex> | ||
</Box> | ||
</> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { zodResolver } from '@hookform/resolvers/zod' | ||
import * as z from 'zod' | ||
|
||
const signInSchema = z.object({ | ||
email: z.string().email('This is not valid email address'), | ||
password: z | ||
.string() | ||
.min(8, { message: 'Password must contain at least 8 character(s)' }) | ||
}) | ||
|
||
export type SignInSchema = z.infer<typeof signInSchema> | ||
export const signInResolver = zodResolver(signInSchema) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import { createServerClient, type CookieOptions } from '@supabase/ssr' | ||
import { type NextRequest, NextResponse } from 'next/server' | ||
|
||
export const createClient = (request: NextRequest) => { | ||
let response = NextResponse.next({ | ||
request: { | ||
headers: request.headers | ||
} | ||
}) | ||
|
||
const supabase = createServerClient( | ||
process.env.NEXT_PUBLIC_SUPABASE_URL!, | ||
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, | ||
{ | ||
cookies: { | ||
get(name: string) { | ||
return request.cookies.get(name)?.value | ||
}, | ||
set(name: string, value: string, options: CookieOptions) { | ||
// If the cookie is updated, update the cookies for the request and response | ||
request.cookies.set({ | ||
name, | ||
value, | ||
...options | ||
}) | ||
response = NextResponse.next({ | ||
request: { | ||
headers: request.headers | ||
} | ||
}) | ||
response.cookies.set({ | ||
name, | ||
value, | ||
...options | ||
}) | ||
}, | ||
remove(name: string, options: CookieOptions) { | ||
// If the cookie is removed, update the cookies for the request and response | ||
request.cookies.set({ | ||
name, | ||
value: '', | ||
...options | ||
}) | ||
response = NextResponse.next({ | ||
request: { | ||
headers: request.headers | ||
} | ||
}) | ||
response.cookies.set({ | ||
name, | ||
value: '', | ||
...options | ||
}) | ||
} | ||
} | ||
} | ||
) | ||
|
||
return { supabase, response } | ||
} |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import { NextResponse, type NextRequest } from 'next/server' | ||
import { createClient } from '@/auth/supabase/middleware' | ||
|
||
export async function middleware(request: NextRequest) { | ||
const { supabase, response } = createClient(request) | ||
|
||
// Refresh session if expired - required for Server Components | ||
// https://supabase.com/docs/guides/auth/auth-helpers/nextjs#managing-session-with-middleware | ||
const { | ||
error, | ||
data: { session } | ||
} = await supabase.auth.getSession() | ||
|
||
const loginUri = '/auth/login' | ||
|
||
if (request.nextUrl.pathname.startsWith(loginUri)) { | ||
return response | ||
} | ||
if (error || !session) { | ||
return NextResponse.redirect(new URL(loginUri, request.url)) | ||
} | ||
|
||
return response | ||
} |