diff --git a/.vscode/settings.json b/.vscode/settings.json index c389dd05..bda6cc30 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,6 @@ { "editor.defaultFormatter": "biomejs.biome", "editor.codeActionsOnSave": { - "quickfix.biome": "explicit", "source.organizeImports.biome": "explicit" }, "[json]": { diff --git a/apps/api/v1/index.ts b/apps/api/v1/index.ts index d6e1b2c8..fcf514eb 100644 --- a/apps/api/v1/index.ts +++ b/apps/api/v1/index.ts @@ -1,13 +1,11 @@ import { Hono } from 'hono' import { authMiddleware } from './middlewares/auth' import authApp from './routes/auth' +import usersApp from './routes/users' export const hono = new Hono() -hono.use('*', authMiddleware()) - -hono.get('/', (c) => { - return c.json({ message: 'Hello Hono!' }) -}) +hono.use('*', authMiddleware) hono.route('/auth', authApp) +hono.route('/users', usersApp) diff --git a/apps/api/v1/middlewares/auth.ts b/apps/api/v1/middlewares/auth.ts index 6176b761..7c0acb55 100644 --- a/apps/api/v1/middlewares/auth.ts +++ b/apps/api/v1/middlewares/auth.ts @@ -1,3 +1,31 @@ -import { clerkMiddleware } from '@hono/clerk-auth' +import type { User } from '@/prisma/generated/zod' +import { clerkMiddleware, getAuth } from '@hono/clerk-auth' +import { createMiddleware } from 'hono/factory' +import { findUserById } from '../services/user.service' -export const authMiddleware = clerkMiddleware +declare module 'hono' { + interface ContextVariableMap { + user: User | null + userId: string | null + } +} + +export const authMiddleware = createMiddleware(async (c, next) => { + await clerkMiddleware()(c, () => Promise.resolve()) + const auth = getAuth(c) + + if (!auth?.userId) { + c.set('userId', null) + c.set('user', null) + return c.json({ message: 'unauthorized' }, 401) + } + + c.set('userId', auth.userId) + + const user = await findUserById(auth.userId) + + c.set('user', user) + c.header('x-user-id', auth.userId) + + await next() +}) diff --git a/apps/api/v1/routes/auth.ts b/apps/api/v1/routes/auth.ts index 3d479e9f..a3f11ed8 100644 --- a/apps/api/v1/routes/auth.ts +++ b/apps/api/v1/routes/auth.ts @@ -1,6 +1,5 @@ import { getAuth } from '@hono/clerk-auth' import { Hono } from 'hono' -import { HTTPException } from 'hono/http-exception' import { findUserById } from '../services/user.service' const router = new Hono() @@ -9,13 +8,13 @@ router.get('/me', async (c) => { const auth = getAuth(c) if (!auth?.userId) { - throw new HTTPException(401, { message: 'unauthorized' }) + return c.json({ message: 'unauthorized' }, 401) } const user = await findUserById(auth.userId) if (!user) { - throw new HTTPException(401, { message: 'unauthorized' }) + return c.json({ message: 'unauthorized' }, 401) } return c.json(user) diff --git a/apps/api/v1/routes/users.ts b/apps/api/v1/routes/users.ts new file mode 100644 index 00000000..5a564086 --- /dev/null +++ b/apps/api/v1/routes/users.ts @@ -0,0 +1,26 @@ +import { zValidator } from '@hono/zod-validator' +import { Hono } from 'hono' +import { createUser } from '../services/user.service' +import { zCreateUser } from '../validation' + +const router = new Hono() + +router.post('/', zValidator('json', zCreateUser), async (c) => { + const existingUser = c.get('user') + + if (existingUser) { + return c.json({ message: 'user already exists' }, 409) + } + + const userId = c.get('userId')! + const data = c.req.valid('json') + + try { + const user = await createUser({ ...data, id: userId }) + return c.json(user, 201) + } catch (e) { + return c.json({ userId, message: 'failed to create user', cause: e }, 500) + } +}) + +export default router diff --git a/apps/api/v1/services/user.service.ts b/apps/api/v1/services/user.service.ts index 74c128f6..810d3a89 100644 --- a/apps/api/v1/services/user.service.ts +++ b/apps/api/v1/services/user.service.ts @@ -1,4 +1,5 @@ import prisma from '@/lib/prisma' +import type { CreateUser } from '../validation' export async function findUserById(id: string) { return await prisma.user.findUnique({ @@ -7,3 +8,13 @@ export async function findUserById(id: string) { }, }) } + +export async function createUser( + data: CreateUser & { + id?: string + }, +) { + return await prisma.user.create({ + data, + }) +} diff --git a/apps/api/v1/validation/index.ts b/apps/api/v1/validation/index.ts index a6c270f9..bd8dc106 100644 --- a/apps/api/v1/validation/index.ts +++ b/apps/api/v1/validation/index.ts @@ -1 +1,2 @@ -export * from './auth.zod' \ No newline at end of file +export * from './auth.zod' +export * from './user.zod' diff --git a/apps/api/v1/validation/user.zod.ts b/apps/api/v1/validation/user.zod.ts new file mode 100644 index 00000000..24936908 --- /dev/null +++ b/apps/api/v1/validation/user.zod.ts @@ -0,0 +1,9 @@ +import { z } from 'zod' + +export const zCreateUser = z.object({ + email: z.string().email(), + password: z.string().min(6), + name: z.string().min(3), +}) + +export type CreateUser = z.infer