@kazion/create-app
Version:
A cli tool to help you get started with graphql and rest api's with typescript
138 lines (124 loc) • 3.56 kB
text/typescript
import {
comparePassword,
createJWT,
decodeJWT,
formatZodError,
hashPassword,
verifyJWT,
} from '@kazion/node-utils'
import { ZodError } from 'zod'
import { BadRequest, ServerError, UnAuthenticated } from '~/errors'
import { ERROR_MESSAGES } from '~/errors/errorMessages'
import {
UserLoginSchema,
type UserLoginPayload,
type UserPayload,
} from '~/modules/auth/auth.schema'
import { UserSchema } from '~/modules/users/user.schema'
import {
createUser,
getUserByEmail,
getUserById,
updateUser,
} from '~/modules/users/user.services'
import type { Args, ResolverHandler } from '~/types/server.types'
export const registerUserHandler: ResolverHandler = async (
_: any,
args: Args<UserPayload>,
{ prisma },
) => {
try {
const cleanedData = await UserSchema.parseAsync(args.input)
const foundUser = await getUserByEmail(prisma, cleanedData.phoneNumber)
if (foundUser) {
throw new BadRequest('Account already exists')
}
args.input.password = await hashPassword(args.input.password)
await createUser(prisma, args.input)
return { ok: true }
} catch (error: any) {
if (error instanceof ZodError) {
const message = formatZodError(error)
throw new BadRequest(message)
}
throw new ServerError()
}
}
export const loginHandler: ResolverHandler = async (
_,
args: Args<UserLoginPayload>,
cxt,
) => {
try {
const cleanedData = await UserLoginSchema.parseAsync(args.input)
const user = await getUserByEmail(cxt.prisma, cleanedData.phoneNumber)
if (!user) {
throw new UnAuthenticated(ERROR_MESSAGES.UNAUTHENTICATED)
}
if (!(await comparePassword(args.input.password, user.password))) {
throw new UnAuthenticated(ERROR_MESSAGES.UNAUTHENTICATED)
}
const token = await createJWT(user.id, {
secret: process.env.SECRET_KEY as string,
})
const updatedUser = await updateUser(cxt.prisma, user.id, {
lastLogin: new Date(),
})
void cxt.reply.cookie('token', token, {
httpOnly: true,
maxAge: 1000 * 60 * 60 * 24 * 7, // 7 days
})
return { ...updatedUser }
} catch (error: any) {
if (error instanceof ZodError) {
const message = formatZodError(error)
throw new BadRequest(message)
}
throw new ServerError()
}
}
export const verifyTokenHandler: ResolverHandler = async (
__,
_args,
{ req, reply, prisma },
) => {
const authHeader = req?.cookies?.token
const inputToken = _args?.input?.token
if (!inputToken && !authHeader) {
throw new UnAuthenticated(ERROR_MESSAGES.UNAUTHENTICATED)
}
const { uid } = await verifyJWT(authHeader ?? inputToken, {
secret: process.env.SECRET_KEY as string,
})
const user = await getUserById(prisma, uid)
void reply.cookie('token', authHeader ?? inputToken, {
httpOnly: true,
maxAge: 1000 * 60 * 60 * 24 * 7, // 7 days
})
return user
}
export const logoutHandler: ResolverHandler = async (
__,
_,
{ prisma, reply },
) => {
try {
const authHeader = reply?.cookies?.token
if (!authHeader) {
throw new UnAuthenticated(ERROR_MESSAGES.UNAUTHENTICATED)
}
const { uid } = decodeJWT(authHeader)
const user = await getUserById(prisma, uid)
if (!user) {
throw new UnAuthenticated(ERROR_MESSAGES.UNAUTHENTICATED)
}
await updateUser(prisma, user.id, user)
void reply.clearCookie('token', {
httpOnly: true,
maxAge: 1000 * 60 * 60 * 24 * 7, // 7 days
})
return { ok: true }
} catch (error) {
return { ok: false }
}
}