From 31bc698f5ff79cc33bca3b4451471cfd99cbc28d Mon Sep 17 00:00:00 2001 From: Baptiste Arnaud Date: Thu, 13 Feb 2025 17:28:08 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=A7=20Improve=20Sentry=20error=20manag?= =?UTF-8?q?ement?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/builder/package.json | 2 +- apps/builder/sentry.server.config.ts | 28 ++++++++++++++++-- .../auth/helpers/getAuthenticatedUser.ts | 2 ++ .../editor/providers/TypebotProvider.tsx | 8 ++--- .../components/CreateNewTypebotButtons.tsx | 6 ++-- .../src/features/typebot/api/importTypebot.ts | 5 ---- apps/builder/src/helpers/server/trpc.ts | 11 ++++--- .../src/pages/api/auth/[...nextauth].ts | 5 ++++ apps/viewer/sentry.server.config.ts | 22 ++++++++++++-- apps/viewer/src/helpers/server/trpc.ts | 6 +--- bun.lockb | Bin 1229400 -> 1229400 bytes yarn.lock | 12 ++++---- 12 files changed, 70 insertions(+), 37 deletions(-) diff --git a/apps/builder/package.json b/apps/builder/package.json index 242f04099..d45053d63 100644 --- a/apps/builder/package.json +++ b/apps/builder/package.json @@ -92,7 +92,7 @@ "tinycolor2": "1.6.0", "unsplash-js": "7.0.18", "use-debounce": "9.0.4", - "zod-validation-error": "3.0.3", + "zod-validation-error": "3.4.0", "zustand": "4.5.0", "@typebot.io/workspaces": "workspace:*", "@typebot.io/blocks-integrations": "workspace:*", diff --git a/apps/builder/sentry.server.config.ts b/apps/builder/sentry.server.config.ts index a003ce61c..b293dc89f 100644 --- a/apps/builder/sentry.server.config.ts +++ b/apps/builder/sentry.server.config.ts @@ -1,8 +1,32 @@ import * as Sentry from "@sentry/nextjs"; +import type { TRPCError } from "@trpc/server"; + +const ignoreTrpcMessages = [ + "potential malicious typebot", + "typebot not found", + "workspace not found", +]; Sentry.init({ dsn: process.env.SENTRY_DSN, - tracesSampleRate: 0.5, environment: process.env.NODE_ENV, - ignoreErrors: ["not found"], + beforeSend: (event, hint) => { + const exception = hint.originalException; + if (isTrpcError(exception)) { + if (ignoreTrpcMessages.includes(exception.message.toLowerCase())) + return null; + if ( + exception.code === "BAD_REQUEST" && + exception.cause?.name === "ZodError" && + event.contexts?.trpc?.procedure_path === "typebot.importTypebot" + ) + return null; + } + return event; + }, }); + +const isTrpcError = (err: unknown): err is TRPCError => { + if (!err || typeof err !== "object") return false; + return "name" in err && err.name === "TRPCError"; +}; diff --git a/apps/builder/src/features/auth/helpers/getAuthenticatedUser.ts b/apps/builder/src/features/auth/helpers/getAuthenticatedUser.ts index 5d482a1a6..a3eb82e9e 100644 --- a/apps/builder/src/features/auth/helpers/getAuthenticatedUser.ts +++ b/apps/builder/src/features/auth/helpers/getAuthenticatedUser.ts @@ -19,6 +19,7 @@ export const getAuthenticatedUser = async ( | Prisma.User | undefined); if (!user || !("id" in user)) return; + Sentry.setUser({ id: user.id }); return user; }; @@ -29,6 +30,7 @@ const authenticateByToken = async ( const user = (await prisma.user.findFirst({ where: { apiTokens: { some: { token: apiToken } } }, })) as Prisma.User; + Sentry.setUser({ id: user.id }); return user; }; diff --git a/apps/builder/src/features/editor/providers/TypebotProvider.tsx b/apps/builder/src/features/editor/providers/TypebotProvider.tsx index 1946688d5..510e447a0 100644 --- a/apps/builder/src/features/editor/providers/TypebotProvider.tsx +++ b/apps/builder/src/features/editor/providers/TypebotProvider.tsx @@ -122,7 +122,7 @@ export const TypebotProvider = ({ title: "Could not fetch typebot", description: error.message, details: { - content: JSON.stringify(error.data?.zodError?.fieldErrors, null, 2), + content: error.data?.zodError ?? "", lang: "json", }, }); @@ -146,11 +146,7 @@ export const TypebotProvider = ({ title: "Could not fetch published typebot", description: error.message, details: { - content: JSON.stringify( - error.data?.zodError?.fieldErrors, - null, - 2, - ), + content: error.data?.zodError ?? "", lang: "json", }, }); diff --git a/apps/builder/src/features/templates/components/CreateNewTypebotButtons.tsx b/apps/builder/src/features/templates/components/CreateNewTypebotButtons.tsx index 4c3eabe8b..18fa80450 100644 --- a/apps/builder/src/features/templates/components/CreateNewTypebotButtons.tsx +++ b/apps/builder/src/features/templates/components/CreateNewTypebotButtons.tsx @@ -15,6 +15,7 @@ import { useTranslate } from "@tolgee/react"; import type { Typebot } from "@typebot.io/typebot/schemas/typebot"; import { useRouter } from "next/router"; import React, { useState } from "react"; +import { toast } from "sonner"; import { ImportTypebotFromFileButton } from "./ImportTypebotFromFileButton"; import { TemplatesModal } from "./TemplatesModal"; @@ -54,10 +55,7 @@ export const CreateNewTypebotButtons = () => { setIsLoading(true); }, onError: (error) => { - showToast({ - title: "Failed to import bot", - description: error.message, - }); + toast.error(error.data?.zodError ?? error.message, { duration: 10000 }); }, onSuccess: (data) => { router.push({ diff --git a/apps/builder/src/features/typebot/api/importTypebot.ts b/apps/builder/src/features/typebot/api/importTypebot.ts index 049dfe353..8741955fc 100644 --- a/apps/builder/src/features/typebot/api/importTypebot.ts +++ b/apps/builder/src/features/typebot/api/importTypebot.ts @@ -137,16 +137,12 @@ export const importTypebot = authenticatedProcedure const newBotId = createId(); - console.log("ORIGINAL ICON", typebot.icon); - let duplicatingBot = await duplicateTypebotS3Objects({ typebot, newTypebotId: newBotId, newWorkspaceId: workspaceId, }); - console.log("DUPLICATED ICON", duplicatingBot.icon); - duplicatingBot = await migrateImportingTypebot(duplicatingBot); const groups = ( @@ -155,7 +151,6 @@ export const importTypebot = authenticatedProcedure : [] ) as TypebotV6["groups"]; - console.log("DUPLICATED ICON", duplicatingBot.icon); const newTypebot = await prisma.typebot.create({ data: { id: newBotId, diff --git a/apps/builder/src/helpers/server/trpc.ts b/apps/builder/src/helpers/server/trpc.ts index 7337fa879..900a3c1c6 100644 --- a/apps/builder/src/helpers/server/trpc.ts +++ b/apps/builder/src/helpers/server/trpc.ts @@ -3,6 +3,7 @@ import { TRPCError, initTRPC } from "@trpc/server"; import type { OpenApiMeta } from "@typebot.io/trpc-openapi/types"; import superjson from "superjson"; import { ZodError } from "zod"; +import { fromError } from "zod-validation-error"; import type { Context } from "./context"; const t = initTRPC @@ -16,7 +17,9 @@ const t = initTRPC data: { ...shape.data, zodError: - error.cause instanceof ZodError ? error.cause.flatten() : null, + error.cause instanceof ZodError + ? fromError(error.cause).message + : null, }, }; }, @@ -35,11 +38,7 @@ const isAuthed = t.middleware(({ next, ctx }) => { }); }); -const sentryMiddleware = t.middleware( - Sentry.trpcMiddleware({ - attachRpcInput: true, - }), -); +const sentryMiddleware = t.middleware(Sentry.trpcMiddleware()); export const middleware = t.middleware; diff --git a/apps/builder/src/pages/api/auth/[...nextauth].ts b/apps/builder/src/pages/api/auth/[...nextauth].ts index 41a5d9e1c..67c279819 100644 --- a/apps/builder/src/pages/api/auth/[...nextauth].ts +++ b/apps/builder/src/pages/api/auth/[...nextauth].ts @@ -1,5 +1,6 @@ import { getNewUserInvitations } from "@/features/auth/helpers/getNewUserInvitations"; import { sendVerificationRequest } from "@/features/auth/helpers/sendVerificationRequest"; +import * as Sentry from "@sentry/nextjs"; import { env } from "@typebot.io/env"; import { getIp } from "@typebot.io/lib/getIp"; import { mockedUser } from "@typebot.io/lib/mockedUser"; @@ -183,7 +184,11 @@ export const getAuthOptions = ({ error: "/signin", }, events: { + signIn({ user }) { + Sentry.setUser({ id: user.id }); + }, async signOut({ session }) { + Sentry.setUser(null); await trackEvents([ { name: "User logged out", diff --git a/apps/viewer/sentry.server.config.ts b/apps/viewer/sentry.server.config.ts index a8117a0f1..dcd1c6038 100644 --- a/apps/viewer/sentry.server.config.ts +++ b/apps/viewer/sentry.server.config.ts @@ -1,8 +1,26 @@ import * as Sentry from "@sentry/nextjs"; +import type { TRPCError } from "@trpc/server"; + +const ignoreTrpcMessages = [ + "bot is now closed", + "not found", + "timeout reached", +]; Sentry.init({ dsn: process.env.SENTRY_DSN, - tracesSampleRate: 0.5, environment: process.env.NODE_ENV, - ignoreErrors: ["bot is now closed", "session not found", "timeout reached"], + beforeSend: (event, hint) => { + const exception = hint.originalException; + if (isTrpcError(exception)) { + if (ignoreTrpcMessages.includes(exception.message.toLowerCase())) + return null; + } + return event; + }, }); + +const isTrpcError = (err: unknown): err is TRPCError => { + if (!err || typeof err !== "object") return false; + return "name" in err && err.name === "TRPCError"; +}; diff --git a/apps/viewer/src/helpers/server/trpc.ts b/apps/viewer/src/helpers/server/trpc.ts index c434191b9..331efdac5 100644 --- a/apps/viewer/src/helpers/server/trpc.ts +++ b/apps/viewer/src/helpers/server/trpc.ts @@ -16,11 +16,7 @@ const injectUser = t.middleware(({ next, ctx }) => { }); }); -const sentryMiddleware = t.middleware( - Sentry.trpcMiddleware({ - attachRpcInput: true, - }), -); +const sentryMiddleware = t.middleware(Sentry.trpcMiddleware()); export const middleware = t.middleware; diff --git a/bun.lockb b/bun.lockb index bfde3319bdc0974fbb5d845f7561c297d46ef151..84b0a617a9016c99615568c17347fd7a709cb099 100755 GIT binary patch delta 217 zcmcc7;(epVdxD;Vr%3nh=S$x<_Ik~oYUY)-ZDSc`mX z$F$Xt*-U>j%Sn^=9s0~b%mT!$K+Lv%hd%q-EQgfG7r&}Ty!iHQ)#9Bo)pbUm!tR38 z^BYCCRowY9D?H$J2z$t1w|CD{o_v2Dzug^-iyeqL zfS41Axqz4(h-A*woBd=j4z))NmW2;`UVpLmhJNb Igmx7I0H|?c7XSbN delta 217 zcmcc7;(epVdxD-qm<5PgftYRk4t@5uSq=p*m)9QFx;uU6rysHIK8Nq`?wtK^ z?}NT&%;NHYU+m0_w=K}$S0h}wMtAe4NY=CO|Fj14AKLbIJ?nAzq=V1hU&^;Hoy88s z96-zo#9Tnk4a7V^%)5Q*EWScJW&=IrcFCK3K+F%s0^22T3dWaDU$TN=4.3.9, zustand@^4.4.7: version "4.5.0"