112 lines
2.4 KiB
TypeScript
112 lines
2.4 KiB
TypeScript
'use server'
|
|
|
|
import { signIn } from '@/auth'
|
|
import { ResultCode, getStringFromBuffer } from '@/lib/utils'
|
|
import { z } from 'zod'
|
|
import { kv } from '@vercel/kv'
|
|
import { getUser } from '../login/actions'
|
|
import { AuthError } from 'next-auth'
|
|
|
|
export async function createUser(
|
|
email: string,
|
|
hashedPassword: string,
|
|
salt: string
|
|
) {
|
|
const existingUser = await getUser(email)
|
|
|
|
if (existingUser) {
|
|
return {
|
|
type: 'error',
|
|
resultCode: ResultCode.UserAlreadyExists
|
|
}
|
|
} else {
|
|
const user = {
|
|
id: crypto.randomUUID(),
|
|
email,
|
|
password: hashedPassword,
|
|
salt
|
|
}
|
|
|
|
await kv.hmset(`user:${email}`, user)
|
|
|
|
return {
|
|
type: 'success',
|
|
resultCode: ResultCode.UserCreated
|
|
}
|
|
}
|
|
}
|
|
|
|
interface Result {
|
|
type: string
|
|
resultCode: ResultCode
|
|
}
|
|
|
|
export async function signup(
|
|
_prevState: Result | undefined,
|
|
formData: FormData
|
|
): Promise<Result | undefined> {
|
|
const email = formData.get('email') as string
|
|
const password = formData.get('password') as string
|
|
|
|
const parsedCredentials = z
|
|
.object({
|
|
email: z.string().email(),
|
|
password: z.string().min(6)
|
|
})
|
|
.safeParse({
|
|
email,
|
|
password
|
|
})
|
|
|
|
if (parsedCredentials.success) {
|
|
const salt = crypto.randomUUID()
|
|
|
|
const encoder = new TextEncoder()
|
|
const saltedPassword = encoder.encode(password + salt)
|
|
const hashedPasswordBuffer = await crypto.subtle.digest(
|
|
'SHA-256',
|
|
saltedPassword
|
|
)
|
|
const hashedPassword = getStringFromBuffer(hashedPasswordBuffer)
|
|
|
|
try {
|
|
const result = await createUser(email, hashedPassword, salt)
|
|
|
|
if (result.resultCode === ResultCode.UserCreated) {
|
|
await signIn('credentials', {
|
|
email,
|
|
password,
|
|
redirect: false
|
|
})
|
|
}
|
|
|
|
return result
|
|
} catch (error) {
|
|
if (error instanceof AuthError) {
|
|
switch (error.type) {
|
|
case 'CredentialsSignin':
|
|
return {
|
|
type: 'error',
|
|
resultCode: ResultCode.InvalidCredentials
|
|
}
|
|
default:
|
|
return {
|
|
type: 'error',
|
|
resultCode: ResultCode.UnknownError
|
|
}
|
|
}
|
|
} else {
|
|
return {
|
|
type: 'error',
|
|
resultCode: ResultCode.UnknownError
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
return {
|
|
type: 'error',
|
|
resultCode: ResultCode.InvalidCredentials
|
|
}
|
|
}
|
|
}
|