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:
parent
2c3da5425b
commit
b0e474677e
8 changed files with 187 additions and 70 deletions
|
@ -1,7 +1,6 @@
|
|||
import NextAuth, { type SessionStrategy } from 'next-auth'
|
||||
import CredentialsProvider from 'next-auth/providers/credentials'
|
||||
import { z } from 'zod'
|
||||
import type { SharedType } from '@maybe-finance/shared'
|
||||
import { PrismaClient } from '@prisma/client'
|
||||
import { PrismaAdapter } from '@auth/prisma-adapter'
|
||||
import axios from 'axios'
|
||||
|
@ -36,19 +35,20 @@ export const authOptions = {
|
|||
password: { label: 'Password', type: 'password' },
|
||||
},
|
||||
async authorize(credentials) {
|
||||
console.log('inside the authorize method')
|
||||
const parsedCredentials = z
|
||||
.object({
|
||||
name: z.string().optional(),
|
||||
email: z.string().email(),
|
||||
password: z.string().min(6),
|
||||
provider: z.string().optional(),
|
||||
})
|
||||
.safeParse(credentials)
|
||||
|
||||
console.log("here's the credentials", parsedCredentials)
|
||||
|
||||
if (parsedCredentials.success) {
|
||||
console.log("Credentials are valid, let's authorize")
|
||||
const { email, password } = parsedCredentials.data
|
||||
console.log('Here are the params', email, password)
|
||||
const { name, email, password } = parsedCredentials.data
|
||||
|
||||
console.log('Hitting endpoint to get user', email)
|
||||
const { data } = await axios.get(`/auth-users`, {
|
||||
params: { email: email },
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
|
@ -56,19 +56,18 @@ export const authOptions = {
|
|||
|
||||
const user = data.data['json']
|
||||
|
||||
console.log('This is User', user)
|
||||
console.log('here is the user', user)
|
||||
|
||||
if (!user.id) {
|
||||
console.log('User does not exist, creating new user')
|
||||
if (!user) {
|
||||
console.log('User does not exist, creating user')
|
||||
const hashedPassword = await bcrypt.hash(password, 10)
|
||||
const { data: newUser } = await axios.post<SharedType.AuthUser>(
|
||||
'/auth-users',
|
||||
{
|
||||
email,
|
||||
password: hashedPassword,
|
||||
}
|
||||
)
|
||||
console.log('Created new user', newUser)
|
||||
console.log('Hitting endpoint to create user', name, email, hashedPassword)
|
||||
const { data } = await axios.post('/auth-users', {
|
||||
name,
|
||||
email,
|
||||
password: hashedPassword,
|
||||
})
|
||||
const newUser = data.data['json']
|
||||
if (newUser) return newUser
|
||||
throw new Error('Could not create user')
|
||||
}
|
||||
|
@ -77,7 +76,6 @@ export const authOptions = {
|
|||
if (passwordsMatch) return user
|
||||
}
|
||||
|
||||
console.log('Invalid credentials')
|
||||
return null
|
||||
},
|
||||
}),
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
import type { ReactElement } from 'react'
|
||||
import { useState } from 'react'
|
||||
import { useState, type ReactElement } from 'react'
|
||||
import { FullPageLayout } from '@maybe-finance/client/features'
|
||||
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 { useRouter } from 'next/router'
|
||||
import { useEffect } from 'react'
|
||||
import Script from 'next/script'
|
||||
import Link from 'next/link'
|
||||
|
||||
export default function LoginPage() {
|
||||
const [email, setEmail] = useState('')
|
||||
|
@ -39,43 +38,59 @@ export default function LoginPage() {
|
|||
strategy="lazyOnload"
|
||||
/>
|
||||
<div className="absolute inset-0 flex flex-col items-center justify-center">
|
||||
<img
|
||||
className="mb-8"
|
||||
src="/assets/maybe.svg"
|
||||
alt="Maybe Finance Logo"
|
||||
height={140}
|
||||
width={140}
|
||||
/>
|
||||
<form className="space-y-4" onSubmit={onSubmit}>
|
||||
<Input
|
||||
type="text"
|
||||
label="Email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.currentTarget.value)}
|
||||
/>
|
||||
<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="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)
|
||||
}
|
||||
/>
|
||||
<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'}
|
||||
>
|
||||
Log in
|
||||
</Button>
|
||||
</form>
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={!isValid}
|
||||
variant={isValid ? 'primary' : 'secondary'}
|
||||
>
|
||||
Log in
|
||||
</Button>
|
||||
<div className="text-sm text-gray-50 pt-2">
|
||||
<div>
|
||||
Don't have an account?{' '}
|
||||
<Link
|
||||
className="hover:text-cyan-400 underline font-medium"
|
||||
href="/register"
|
||||
>
|
||||
Sign up
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
|
|
|
@ -1,11 +1,18 @@
|
|||
import type { ReactElement } from 'react'
|
||||
import { Input, InputPassword } from '@maybe-finance/design-system'
|
||||
import { useState, type ReactElement } from 'react'
|
||||
import { Input, InputPassword, Button } from '@maybe-finance/design-system'
|
||||
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 { useEffect } from 'react'
|
||||
import Script from 'next/script'
|
||||
import Link from 'next/link'
|
||||
|
||||
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 router = useRouter()
|
||||
|
||||
|
@ -13,13 +20,90 @@ export default function RegisterPage() {
|
|||
if (session) router.push('/')
|
||||
}, [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
|
||||
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>
|
||||
<Input type="text" placeholder="Email" />
|
||||
<InputPassword placeholder="Password" />
|
||||
</div>
|
||||
<>
|
||||
<Script
|
||||
src="https://cdnjs.cloudflare.com/ajax/libs/zxcvbn/4.4.2/zxcvbn.js"
|
||||
strategy="lazyOnload"
|
||||
/>
|
||||
<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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -145,3 +145,20 @@
|
|||
height: 0;
|
||||
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%
|
||||
);
|
||||
}
|
||||
|
|
|
@ -32,6 +32,10 @@ module.exports = merge(designSystemConfig, {
|
|||
wave: '3 wave 0.6s ease-in-out',
|
||||
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: () => {
|
||||
const { white, gray, cyan } = designSystemConfig.theme.colors
|
||||
return {
|
||||
|
|
|
@ -9,12 +9,9 @@ router.get(
|
|||
endpoint.create({
|
||||
resolve: async ({ ctx, req }) => {
|
||||
const email = req.query.email
|
||||
console.log('Going to get the user for email', email)
|
||||
const user = await ctx.authUserService.getByEmail(email as string)
|
||||
console.log('Got the user', user)
|
||||
if (user) return user
|
||||
console.log('No user found')
|
||||
return { data: null }
|
||||
return null
|
||||
},
|
||||
})
|
||||
)
|
||||
|
@ -23,11 +20,13 @@ router.post(
|
|||
'/',
|
||||
endpoint.create({
|
||||
input: z.object({
|
||||
name: z.string(),
|
||||
email: z.string().email(),
|
||||
password: z.string().min(6),
|
||||
}),
|
||||
resolve: async ({ input, ctx }) => {
|
||||
return await ctx.authUserService.create({
|
||||
name: input.name,
|
||||
email: input.email,
|
||||
password: input.password,
|
||||
})
|
||||
|
|
|
@ -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 } })
|
||||
return user
|
||||
}
|
||||
|
|
|
@ -565,7 +565,7 @@ model PlanMilestone {
|
|||
@@map("plan_milestone")
|
||||
}
|
||||
|
||||
// Next Auth Models
|
||||
// NextAuth Models
|
||||
model AuthAccount {
|
||||
id String @id @default(cuid())
|
||||
userId String @map("user_id")
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue