File tree Expand file tree Collapse file tree 15 files changed +400
-65
lines changed Expand file tree Collapse file tree 15 files changed +400
-65
lines changed Original file line number Diff line number Diff line change 22
22
"bcryptjs" : " ^3.0.2" ,
23
23
"class-variance-authority" : " ^0.7.1" ,
24
24
"clsx" : " ^2.1.1" ,
25
+ "iron-session" : " ^8.0.4" ,
25
26
"lucide-react" : " ^0.542.0" ,
26
27
"luxon" : " ^3.7.2" ,
27
- "next" : " 15.5.2" ,
28
+ "next" : " ^15.5.2" ,
29
+ "next-safe-action" : " ^8.0.11" ,
28
30
"react" : " 19.1.0" ,
29
31
"react-dom" : " 19.1.0" ,
30
32
"react-icons" : " ^5.5.0" ,
31
33
"tailwind-merge" : " ^3.3.1" ,
32
34
"viem" : " ^2.37.5" ,
33
- "wagmi" : " ^2.16.9"
35
+ "wagmi" : " ^2.16.9" ,
36
+ "zod" : " ^3.25.76"
34
37
},
35
38
"devDependencies" : {
36
39
"@eslint/eslintrc" : " ^3" ,
Original file line number Diff line number Diff line change
1
+ 'use server' ;
2
+
3
+ import { actionClient } from '@/lib/action' ;
4
+ import { signin } from './logic' ;
5
+ import { signinSchema } from './schema' ;
6
+
7
+ export const signinAction = actionClient
8
+ . inputSchema ( signinSchema )
9
+ . metadata ( { actionName : 'signin' } )
10
+ . action ( async ( { parsedInput } ) => {
11
+ const { email } = parsedInput ;
12
+
13
+ try {
14
+ const result = await signin ( parsedInput ) ;
15
+
16
+ if ( result . success ) {
17
+ return result . data ;
18
+ }
19
+
20
+ throw new Error ( result . error , { cause : { internal : true } } ) ;
21
+ } catch ( err ) {
22
+ const error = err as Error ;
23
+ const cause = error . cause as { internal : boolean } | undefined ;
24
+
25
+ if ( cause ?. internal ) {
26
+ throw new Error ( error . message , { cause : error } ) ;
27
+ }
28
+
29
+ console . error ( 'Sign in error:' , error ) ;
30
+ throw new Error ( 'Something went wrong' ) ;
31
+ }
32
+ } ) ;
Original file line number Diff line number Diff line change
1
+ import 'server-only' ;
2
+
3
+ import { prisma } from "@/lib/prisma" ;
4
+ import { signinInput } from "./schema" ;
5
+ import bcrypt from "bcryptjs" ;
6
+ import { error , Result , success } from "@/lib/result" ;
7
+ import { getSession } from "@/lib/session" ;
8
+ import type { User } from "@prisma/client" ;
9
+
10
+ export async function signin ( input : signinInput ) : Promise < Result < Omit < User , 'password' > > > {
11
+ const { email, password } = input ;
12
+
13
+ const normalisedEmail = email . toLowerCase ( ) . trim ( ) ;
14
+
15
+ //Find user by email
16
+ const user = await prisma . user . findUnique ( {
17
+ where : {
18
+ email : normalisedEmail
19
+ }
20
+ } )
21
+ if ( ! user ) {
22
+ console . error ( 'signin error: User not found' ) ;
23
+ return error ( 'Invalid credentials' ) ;
24
+ }
25
+
26
+ //Verify Password
27
+ const isValidPassword = await bcrypt . compare ( password , user . password ! ) ;
28
+
29
+ if ( ! isValidPassword ) {
30
+ console . error ( 'signin error: Invalid Password' ) ;
31
+ return error ( 'Invalid credentials' ) ;
32
+ }
33
+
34
+ // Omit password from returned user object
35
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
36
+ const { password : _password , ...userWithoutPassword } = user ;
37
+
38
+ //Session
39
+ const session = await getSession ( ) ;
40
+ session . user = { id : userWithoutPassword . id } ;
41
+ await session . save ( ) ;
42
+
43
+ return success ( userWithoutPassword ) ;
44
+ }
Original file line number Diff line number Diff line change
1
+ import { z } from 'zod' ;
2
+
3
+ export const signinSchema = z . object ( {
4
+ email : z . string ( ) . email ( "Invalid Email Format" ) ,
5
+ password : z . string ( ) . min ( 1 , 'Password is required' )
6
+ } ) ;
7
+
8
+ export type signinInput = z . infer < typeof signinSchema >
Original file line number Diff line number Diff line change
1
+ 'use server' ;
2
+
3
+ import { authActionClient } from '@/lib/action' ;
4
+ import { signout } from './logic' ;
5
+
6
+ export const signoutAction = authActionClient . metadata ( { actionName : 'signout' } ) . action ( async ( { ctx } ) => {
7
+ const userId = ctx . session . user . id ;
8
+
9
+ try {
10
+ const result = await signout ( ) ;
11
+
12
+ if ( result . success ) {
13
+ return result . data ;
14
+ }
15
+
16
+ throw new Error ( result . error , { cause : { internal : true } } ) ;
17
+ } catch ( err ) {
18
+ const error = err as Error ;
19
+ const cause = error . cause as { internal : boolean } | undefined ;
20
+
21
+ if ( cause ?. internal ) {
22
+ throw new Error ( error . message , { cause : error } ) ;
23
+ }
24
+
25
+ console . error ( 'Sign out error:' , error , { userId } ) ;
26
+ throw new Error ( 'Something went wrong' ) ;
27
+ }
28
+ } ) ;
Original file line number Diff line number Diff line change
1
+ import 'server-only' ;
2
+
3
+ import { Result , success } from '@/lib/result' ;
4
+ import { getSession } from '@/lib/session' ;
5
+
6
+ export async function signout ( ) : Promise < Result < undefined > > {
7
+ const session = await getSession ( ) ;
8
+ await session . destroy ( ) ;
9
+ return success ( undefined ) ;
10
+ }
Original file line number Diff line number Diff line change
1
+ 'use server' ;
2
+
3
+ import { actionClient } from '@/lib/action' ;
4
+ import { Signup } from './logic' ;
5
+ import { SignupSchema } from './schema' ;
6
+
7
+ export const signupAction = actionClient
8
+ . inputSchema ( SignupSchema )
9
+ . metadata ( { actionName : 'signup' } )
10
+ . action ( async ( { parsedInput } ) => {
11
+ try {
12
+ const result = await Signup ( parsedInput ) ;
13
+
14
+ if ( result . success ) {
15
+ return result . data ;
16
+ }
17
+
18
+ // Set session
19
+ throw new Error ( result . error , { cause : { internal : true } } ) ;
20
+ } catch ( err ) {
21
+ const error = err as Error ;
22
+ const cause = error . cause as { internal : boolean } | undefined ;
23
+
24
+ if ( cause ?. internal ) {
25
+ throw new Error ( error . message , { cause : error } ) ;
26
+ }
27
+
28
+ console . error ( 'Sign up error:' , error ) ;
29
+ throw new Error ( 'Something went wrong' ) ;
30
+ }
31
+ } ) ;
Original file line number Diff line number Diff line change
1
+ import 'server-only' ;
2
+
3
+ import { prisma } from "@/lib/prisma" ;
4
+ import bcrypt from "bcryptjs" ;
5
+ import { signupInput } from "./schema" ;
6
+ import { error , Result , success } from "@/lib/result" ;
7
+ import { getSession } from "@/lib/session" ;
8
+ import type { User } from "@prisma/client" ;
9
+
10
+ export async function Signup ( input : signupInput ) : Promise < Result < User > > {
11
+ const { email, name, password } = input ;
12
+
13
+ const normalisedEmail = email . toLowerCase ( ) . trim ( ) ;
14
+
15
+ const exisitingUser = await prisma . user . findUnique ( {
16
+ where : {
17
+ email : normalisedEmail
18
+ }
19
+ } ) ;
20
+ if ( exisitingUser ) {
21
+ console . error ( 'Singup error: User with this email already exists' )
22
+ return error ( 'Something went wrong' )
23
+ } ;
24
+
25
+ const hashedPassword = await bcrypt . hash ( password , 12 ) ;
26
+ const user = await prisma . user . create ( {
27
+ data : {
28
+ name,
29
+ email : normalisedEmail ,
30
+ password : hashedPassword
31
+ }
32
+ } ) ;
33
+ const session = await getSession ( ) ;
34
+ session . user = { id : user . id } ;
35
+ await session . save ( ) ;
36
+
37
+ return success ( user ) ;
38
+ }
Original file line number Diff line number Diff line change
1
+ import { z } from "zod" ;
2
+
3
+ export const signupSchema = z . object ( {
4
+ name : z . string ( ) . min ( 1 , 'Name is required' ) ,
5
+ email : z . string ( ) . email ( 'Invalid email format' ) ,
6
+ password : z . string ( )
7
+ . min ( 8 , 'Password must be at least 8 characters long' )
8
+ . regex ( / (? = .* [ a - z ] ) / , 'Password must contain at least one lowercase letter' )
9
+ . regex ( / (? = .* [ A - Z ] ) / , 'Password must contain at least one uppercase letter' )
10
+ . regex ( / (? = .* \d ) / , 'Password must contain at least one number' )
11
+
12
+ } )
13
+
14
+ export type signupInput = z . infer < typeof signupSchema >
You can’t perform that action at this time.
0 commit comments