1
0
Fork 0
mirror of https://github.com/maybe-finance/maybe.git synced 2025-08-09 07:25:19 +02:00

minimal register and login

This commit is contained in:
Tyler Myracle 2024-01-12 12:00:36 -06:00
parent 2c3da5425b
commit b0e474677e
8 changed files with 187 additions and 70 deletions

View file

@ -1,7 +1,6 @@
import NextAuth, { type SessionStrategy } from 'next-auth' import NextAuth, { type SessionStrategy } from 'next-auth'
import CredentialsProvider from 'next-auth/providers/credentials' import CredentialsProvider from 'next-auth/providers/credentials'
import { z } from 'zod' import { z } from 'zod'
import type { SharedType } from '@maybe-finance/shared'
import { PrismaClient } from '@prisma/client' import { PrismaClient } from '@prisma/client'
import { PrismaAdapter } from '@auth/prisma-adapter' import { PrismaAdapter } from '@auth/prisma-adapter'
import axios from 'axios' import axios from 'axios'
@ -36,19 +35,20 @@ export const authOptions = {
password: { label: 'Password', type: 'password' }, password: { label: 'Password', type: 'password' },
}, },
async authorize(credentials) { async authorize(credentials) {
console.log('inside the authorize method')
const parsedCredentials = z const parsedCredentials = z
.object({ .object({
name: z.string().optional(),
email: z.string().email(), email: z.string().email(),
password: z.string().min(6), password: z.string().min(6),
provider: z.string().optional(),
}) })
.safeParse(credentials) .safeParse(credentials)
console.log("here's the credentials", parsedCredentials)
if (parsedCredentials.success) { if (parsedCredentials.success) {
console.log("Credentials are valid, let's authorize") const { name, email, password } = parsedCredentials.data
const { email, password } = parsedCredentials.data
console.log('Here are the params', email, password) console.log('Hitting endpoint to get user', email)
const { data } = await axios.get(`/auth-users`, { const { data } = await axios.get(`/auth-users`, {
params: { email: email }, params: { email: email },
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
@ -56,19 +56,18 @@ export const authOptions = {
const user = data.data['json'] const user = data.data['json']
console.log('This is User', user) console.log('here is the user', user)
if (!user.id) { if (!user) {
console.log('User does not exist, creating new user') console.log('User does not exist, creating user')
const hashedPassword = await bcrypt.hash(password, 10) const hashedPassword = await bcrypt.hash(password, 10)
const { data: newUser } = await axios.post<SharedType.AuthUser>( console.log('Hitting endpoint to create user', name, email, hashedPassword)
'/auth-users', const { data } = await axios.post('/auth-users', {
{ name,
email, email,
password: hashedPassword, password: hashedPassword,
} })
) const newUser = data.data['json']
console.log('Created new user', newUser)
if (newUser) return newUser if (newUser) return newUser
throw new Error('Could not create user') throw new Error('Could not create user')
} }
@ -77,7 +76,6 @@ export const authOptions = {
if (passwordsMatch) return user if (passwordsMatch) return user
} }
console.log('Invalid credentials')
return null return null
}, },
}), }),

View file

@ -1,12 +1,11 @@
import type { ReactElement } from 'react' import { useState, type ReactElement } from 'react'
import { useState } from 'react'
import { FullPageLayout } from '@maybe-finance/client/features' import { FullPageLayout } from '@maybe-finance/client/features'
import { Input, InputPassword, Button } from '@maybe-finance/design-system' import { Input, InputPassword, Button } from '@maybe-finance/design-system'
import { AiOutlineLoading3Quarters as LoadingIcon } from 'react-icons/ai'
import { signIn, useSession } from 'next-auth/react' import { signIn, useSession } from 'next-auth/react'
import { useRouter } from 'next/router' import { useRouter } from 'next/router'
import { useEffect } from 'react' import { useEffect } from 'react'
import Script from 'next/script' import Script from 'next/script'
import Link from 'next/link'
export default function LoginPage() { export default function LoginPage() {
const [email, setEmail] = useState('') const [email, setEmail] = useState('')
@ -39,43 +38,59 @@ export default function LoginPage() {
strategy="lazyOnload" strategy="lazyOnload"
/> />
<div className="absolute inset-0 flex flex-col items-center justify-center"> <div className="absolute inset-0 flex flex-col items-center justify-center">
<img <div className="p-px w-96 bg-white bg-opacity-10 card-light rounded-3xl radial-gradient-background">
className="mb-8" <div className="bg-black bg-opacity-75 p-8 rounded-3xl w-full h-full items-center flex flex-col radial-gradient-background-dark">
src="/assets/maybe.svg" <img
alt="Maybe Finance Logo" className="mb-8"
height={140} src="/assets/maybe.svg"
width={140} alt="Maybe Finance Logo"
/> height={140}
<form className="space-y-4" onSubmit={onSubmit}> width={140}
<Input />
type="text" <form className="space-y-4 w-full px-4" onSubmit={onSubmit}>
label="Email" <Input
value={email} type="text"
onChange={(e) => setEmail(e.currentTarget.value)} label="Email"
/> value={email}
onChange={(e) => setEmail(e.currentTarget.value)}
/>
<InputPassword <InputPassword
autoComplete="password" autoComplete="password"
label="Password" label="Password"
value={password} value={password}
showPasswordRequirements={!isValid} showPasswordRequirements={!isValid}
onValidityChange={(checks) => { onValidityChange={(checks) => {
const passwordValid = checks.filter((c) => !c.isValid).length === 0 const passwordValid =
setIsValid(passwordValid) checks.filter((c) => !c.isValid).length === 0
}} setIsValid(passwordValid)
onChange={(e: React.ChangeEvent<HTMLInputElement>) => }}
setPassword(e.target.value) onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
} setPassword(e.target.value)
/> }
/>
<Button <Button
type="submit" type="submit"
disabled={!isValid} disabled={!isValid}
variant={isValid ? 'primary' : 'secondary'} variant={isValid ? 'primary' : 'secondary'}
> >
Log in Log in
</Button> </Button>
</form> <div className="text-sm text-gray-50 pt-2">
<div>
Don&apos;t have an account?{' '}
<Link
className="hover:text-cyan-400 underline font-medium"
href="/register"
>
Sign up
</Link>
</div>
</div>
</form>
</div>
</div>
</div> </div>
</> </>
) )

View file

@ -1,11 +1,18 @@
import type { ReactElement } from 'react' import { useState, type ReactElement } from 'react'
import { Input, InputPassword } from '@maybe-finance/design-system' import { Input, InputPassword, Button } from '@maybe-finance/design-system'
import { FullPageLayout } from '@maybe-finance/client/features' import { FullPageLayout } from '@maybe-finance/client/features'
import { useSession } from 'next-auth/react' import { signIn, useSession } from 'next-auth/react'
import { useRouter } from 'next/router' import { useRouter } from 'next/router'
import { useEffect } from 'react' import { useEffect } from 'react'
import Script from 'next/script'
import Link from 'next/link'
export default function RegisterPage() { export default function RegisterPage() {
const [name, setName] = useState('')
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const [isValid, setIsValid] = useState(false)
const { data: session } = useSession() const { data: session } = useSession()
const router = useRouter() const router = useRouter()
@ -13,13 +20,90 @@ export default function RegisterPage() {
if (session) router.push('/') if (session) router.push('/')
}, [session, router]) }, [session, router])
const onSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
setName('')
setEmail('')
setPassword('')
await signIn('credentials', {
email,
password,
name,
redirect: false,
})
}
// _app.tsx will automatically redirect if not authenticated // _app.tsx will automatically redirect if not authenticated
return ( return (
<div className="absolute inset-0 flex items-center justify-center"> <>
<div className="text-4xl font-bold text-white">THIS IS THE LOGIN PAGE</div> <Script
<Input type="text" placeholder="Email" /> src="https://cdnjs.cloudflare.com/ajax/libs/zxcvbn/4.4.2/zxcvbn.js"
<InputPassword placeholder="Password" /> strategy="lazyOnload"
</div> />
<div className="absolute inset-0 flex flex-col items-center justify-center">
<div className="p-px w-96 bg-white bg-opacity-10 card-light rounded-3xl radial-gradient-background">
<div className="bg-black bg-opacity-75 p-8 rounded-3xl w-full h-full items-center flex flex-col radial-gradient-background-dark">
<img
className="mb-8"
src="/assets/maybe.svg"
alt="Maybe Finance Logo"
height={140}
width={140}
/>
<form className="space-y-4 w-full px-4" onSubmit={onSubmit}>
<Input
type="text"
label="Name"
value={name}
onChange={(e) => setName(e.currentTarget.value)}
/>
<Input
type="text"
label="Email"
value={email}
onChange={(e) => setEmail(e.currentTarget.value)}
/>
<InputPassword
autoComplete="password"
label="Password"
value={password}
showPasswordRequirements={!isValid}
onValidityChange={(checks) => {
const passwordValid =
checks.filter((c) => !c.isValid).length === 0
setIsValid(passwordValid)
}}
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
setPassword(e.target.value)
}
/>
<Button
type="submit"
disabled={!isValid}
variant={isValid ? 'primary' : 'secondary'}
>
Register
</Button>
<div className="text-sm text-gray-50 pt-2">
<div>
Already have an account?{' '}
<Link
className="hover:text-cyan-400 underline font-medium"
href="/login"
>
Sign in
</Link>
</div>
</div>
</form>
</div>
</div>
</div>
</>
) )
} }

View file

@ -145,3 +145,20 @@
height: 0; height: 0;
pointer-events: none; pointer-events: none;
} }
.radial-gradient-background {
background-image: radial-gradient(
60% 200% at 50% 50%,
rgba(67, 97, 238, 0.5) 0%,
transparent 100%
);
}
.radial-gradient-background-dark {
background-image: radial-gradient(
100% 100% at clamp(20%, calc(30% + var(--mx) * 0.05), 40%)
clamp(50%, calc(50% + var(--my) * 0.05), 60%),
#4361ee33 0%,
#16161af4 120%
);
}

View file

@ -32,6 +32,10 @@ module.exports = merge(designSystemConfig, {
wave: '3 wave 0.6s ease-in-out', wave: '3 wave 0.6s ease-in-out',
float: 'float 4s infinite linear', float: 'float 4s infinite linear',
}, },
backgroundImage: {
'login-background':
'radial-gradient(100% 100% at clamp(20%, calc(30% + var(--mx) * 0.05), 40%) clamp(50%, calc(50% + var(--my) * 0.05), 60%), #4361EE33 0%, #16161Af4 120%)',
},
typography: () => { typography: () => {
const { white, gray, cyan } = designSystemConfig.theme.colors const { white, gray, cyan } = designSystemConfig.theme.colors
return { return {

View file

@ -9,12 +9,9 @@ router.get(
endpoint.create({ endpoint.create({
resolve: async ({ ctx, req }) => { resolve: async ({ ctx, req }) => {
const email = req.query.email const email = req.query.email
console.log('Going to get the user for email', email)
const user = await ctx.authUserService.getByEmail(email as string) const user = await ctx.authUserService.getByEmail(email as string)
console.log('Got the user', user)
if (user) return user if (user) return user
console.log('No user found') return null
return { data: null }
}, },
}) })
) )
@ -23,11 +20,13 @@ router.post(
'/', '/',
endpoint.create({ endpoint.create({
input: z.object({ input: z.object({
name: z.string(),
email: z.string().email(), email: z.string().email(),
password: z.string().min(6), password: z.string().min(6),
}), }),
resolve: async ({ input, ctx }) => { resolve: async ({ input, ctx }) => {
return await ctx.authUserService.create({ return await ctx.authUserService.create({
name: input.name,
email: input.email, email: input.email,
password: input.password, password: input.password,
}) })

View file

@ -22,7 +22,7 @@ export class AuthUserService implements IAuthUserService {
}) })
} }
async create(data: Prisma.AuthUserCreateInput): Promise<AuthUser> { async create(data: Prisma.AuthUserCreateInput) {
const user = await this.prisma.authUser.create({ data: { ...data } }) const user = await this.prisma.authUser.create({ data: { ...data } })
return user return user
} }

View file

@ -565,7 +565,7 @@ model PlanMilestone {
@@map("plan_milestone") @@map("plan_milestone")
} }
// Next Auth Models // NextAuth Models
model AuthAccount { model AuthAccount {
id String @id @default(cuid()) id String @id @default(cuid())
userId String @map("user_id") userId String @map("user_id")