From 2ade2f7cedc6526a2dcb0c048c9fc125d2f8a901 Mon Sep 17 00:00:00 2001 From: Dustin Do Date: Thu, 19 Sep 2024 11:11:40 +0700 Subject: [PATCH] feat(api): improve create user --- apps/api/package.json | 9 ++-- apps/api/v1/routes/users.ts | 15 +++++- apps/api/v1/services/clerk.service.ts | 16 +++++++ apps/api/v1/services/user.service.ts | 11 ++++- pnpm-lock.yaml | 67 ++++++++++++++++++++++++++- 5 files changed, 109 insertions(+), 9 deletions(-) create mode 100644 apps/api/v1/services/clerk.service.ts diff --git a/apps/api/package.json b/apps/api/package.json index e5ff43e5..a57e310c 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -19,6 +19,7 @@ "@6pm/utilities": "workspace:^", "@6pm/validation": "workspace:^", "@clerk/backend": "^1.2.4", + "@clerk/clerk-sdk-node": "^5.0.42", "@hono/clerk-auth": "^2.0.0", "@hono/node-server": "^1.11.4", "@hono/zod-validator": "^0.2.2", @@ -28,14 +29,14 @@ "hono": "^4.4.8", "next": "^14.2.4", "openai": "^4.52.7", - "pino-pretty": "^11.2.1", "pino": "^9.2.0", + "pino-pretty": "^11.2.1", "prisma": "5.19.0", - "react-dom": "18.3.1", "react": "18.3.1", + "react-dom": "18.3.1", "svix": "^1.28.0", - "zod-prisma-types": "^3.1.8", - "zod": "^3.23.8" + "zod": "^3.23.8", + "zod-prisma-types": "^3.1.8" }, "devDependencies": { "@types/node": "18.11.18", diff --git a/apps/api/v1/routes/users.ts b/apps/api/v1/routes/users.ts index 96f6aced..b7db467b 100644 --- a/apps/api/v1/routes/users.ts +++ b/apps/api/v1/routes/users.ts @@ -1,8 +1,10 @@ import { zCreateUser } from '@6pm/validation' import { zValidator } from '@hono/zod-validator' import { Hono } from 'hono' +import { getLogger } from '../../lib/log' import { getAuthUser } from '../middlewares/auth' import { bootstrapUserDefaultCategories } from '../services/category.service' +import { deleteClerkUser } from '../services/clerk.service' import { createUser } from '../services/user.service' import { bootstrapUserDefaultWalletAccounts } from '../services/wallet.service' import { zDeviceCurrencyHeader, zDeviceLanguageHeader } from './utils' @@ -16,12 +18,19 @@ const router = new Hono().post( const existingUser = getAuthUser(c) const deviceLanguage = c.req.valid('header')['x-device-language'] const deviceCurrency = c.req.valid('header')['x-device-currency'] + const logger = getLogger('POST /users') if (existingUser) { return c.json({ message: 'user already exists' }, 409) } - const userId = c.get('userId')! + const userId = c.get('userId') + + if (!userId) { + logger.warn('Clerk userId not found from headers while creating user') + return c.json({ message: 'unauthorized' }, 401) + } + const data = c.req.valid('json') try { @@ -39,6 +48,10 @@ const router = new Hono().post( return c.json(user, 201) } catch (e) { + logger.error('Failed to create user %o', e) + + await deleteClerkUser(userId) + return c.json({ userId, message: 'failed to create user', cause: e }, 500) } }, diff --git a/apps/api/v1/services/clerk.service.ts b/apps/api/v1/services/clerk.service.ts new file mode 100644 index 00000000..27cd7105 --- /dev/null +++ b/apps/api/v1/services/clerk.service.ts @@ -0,0 +1,16 @@ +import { clerkClient } from '@clerk/clerk-sdk-node' +import { getLogger } from '../../lib/log' + +export async function deleteClerkUser(userId: string) { + const logger = getLogger(`clerk.service:${deleteClerkUser.name}:${userId}`) + + try { + const deletedUser = await clerkClient.users.deleteUser(userId) + logger.debug(`Deleted user %o`, deletedUser) + return deletedUser + } catch (error) { + logger.error(`Failed to delete user with id: ${userId}`) + logger.debug(error) + throw error + } +} diff --git a/apps/api/v1/services/user.service.ts b/apps/api/v1/services/user.service.ts index 7c8e867d..eea729cf 100644 --- a/apps/api/v1/services/user.service.ts +++ b/apps/api/v1/services/user.service.ts @@ -18,9 +18,16 @@ export async function findUserByEmail(email: string) { } export async function createUser({ data }: { data: CreateUser }) { - return await prisma.user.create({ - data, + const user = await prisma.user.upsert({ + where: { + id: data.id, + email: data.email, + }, + create: data, + update: data, }) + + return user } export async function deleteUser(userId: string) { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b1f420e6..99f96891 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -35,6 +35,9 @@ importers: '@clerk/backend': specifier: ^1.2.4 version: 1.8.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@clerk/clerk-sdk-node': + specifier: ^5.0.42 + version: 5.0.42(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@hono/clerk-auth': specifier: ^2.0.0 version: 2.0.0(@clerk/backend@1.8.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(hono@4.5.9) @@ -1271,6 +1274,10 @@ packages: cpu: [x64] os: [win32] + '@clerk/backend@1.13.2': + resolution: {integrity: sha512-VpAkb/P/fybC+Rv1uEuUiMXTVxmnl1Umj9wMfS0ZIPx/hu6t3PLVZbGYeNwQULr6mUE5qhomJoEkTsqGyaR8Pg==} + engines: {node: '>=18.17.0'} + '@clerk/backend@1.8.3': resolution: {integrity: sha512-JKQmGHTjMtCKMjEZEO8z2uqg/gs6IeMWJuEq57xapRs+cwNXb7ny0+QPSk22VYd+h2+qt31rX9bbWlUEyjeEmw==} engines: {node: '>=18.17.0'} @@ -1306,6 +1313,10 @@ packages: react: '>=18 || >=19.0.0-beta' react-dom: '>=18 || >=19.0.0-beta' + '@clerk/clerk-sdk-node@5.0.42': + resolution: {integrity: sha512-xUpShwFT1qpONWT2icDOa3tOO80+OJVqjKl3iPcYFl3OXOwth0vkyO+MUe6VEUuwaSZSZqznc1NR7OwRQT7IwA==} + engines: {node: '>=18.17.0'} + '@clerk/localizations@2.7.0': resolution: {integrity: sha512-Fc50AhPX9Hc2ptOjtXGHa7aBkAfsZI4MlV+6xSvwCo2j+0Hz+YGbIpTILYClTcZLXjZvoaMw5EJMvH8vmlzFvQ==} engines: {node: '>=18.17.0'} @@ -1322,10 +1333,26 @@ packages: react-dom: optional: true + '@clerk/shared@2.8.1': + resolution: {integrity: sha512-8LxnrQDj9xdKcOFQa4FSKb3P5loZeLMiynzD9yBJgCBTCJ8dNV//qySlP6xK6z172L8RwnDk29yO9J+v+FS9Sw==} + engines: {node: '>=18.17.0'} + peerDependencies: + react: '>=18 || >=19.0.0-beta' + react-dom: '>=18 || >=19.0.0-beta' + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + '@clerk/types@4.16.0': resolution: {integrity: sha512-SoE6jFT7UuVtAbA6rSLwAk86VXnJhV2g0+GKQxxa6UFaXIm/s65jLVFpxCDdDZ0RJu2sBrMNfsWQHYyy06nRug==} engines: {node: '>=18.17.0'} + '@clerk/types@4.21.0': + resolution: {integrity: sha512-yyPNF4agzub9zXOht9Bk8HG+OkHfLKIpsQuTCiZJszehcKNUqKvJZRxUwdRREBYnBdMl632PKCwbckZsChVxVQ==} + engines: {node: '>=18.17.0'} + '@egjs/hammerjs@2.0.17': resolution: {integrity: sha512-XQsZgjm2EcVUiZQf11UBJQfmZeEmOW8DpI1gsFeln6w0ae0ii4dMQEQ0kjl6DspdWX1aGY1/loyXnP0JS06e/A==} engines: {node: '>=0.8.0'} @@ -8601,6 +8628,17 @@ snapshots: '@biomejs/cli-win32-x64@1.8.3': optional: true + '@clerk/backend@1.13.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@clerk/shared': 2.8.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@clerk/types': 4.21.0 + cookie: 0.5.0 + snakecase-keys: 5.4.4 + tslib: 2.4.1 + transitivePeerDependencies: + - react + - react-dom + '@clerk/backend@1.8.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@clerk/shared': 2.5.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -8665,6 +8703,16 @@ snapshots: react-dom: 18.3.1(react@18.3.1) tslib: 2.4.1 + '@clerk/clerk-sdk-node@5.0.42(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@clerk/backend': 1.13.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@clerk/shared': 2.8.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@clerk/types': 4.21.0 + tslib: 2.4.1 + transitivePeerDependencies: + - react + - react-dom + '@clerk/localizations@2.7.0': dependencies: '@clerk/types': 4.16.0 @@ -8680,10 +8728,25 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) + '@clerk/shared@2.8.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@clerk/types': 4.21.0 + glob-to-regexp: 0.4.1 + js-cookie: 3.0.5 + std-env: 3.7.0 + swr: 2.2.5(react@18.3.1) + optionalDependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + '@clerk/types@4.16.0': dependencies: csstype: 3.1.1 + '@clerk/types@4.21.0': + dependencies: + csstype: 3.1.1 + '@egjs/hammerjs@2.0.17': dependencies: '@types/hammerjs': 2.0.45 @@ -13841,7 +13904,7 @@ snapshots: lower-case@2.0.2: dependencies: - tslib: 2.4.1 + tslib: 2.7.0 lowercase-keys@3.0.0: {} @@ -14235,7 +14298,7 @@ snapshots: no-case@3.0.4: dependencies: lower-case: 2.0.2 - tslib: 2.4.1 + tslib: 2.7.0 nocache@3.0.4: {}