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 bfde3319b..84b0a617a 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/yarn.lock b/yarn.lock index 9a56d7abf..3146f646c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1,6 +1,6 @@ # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. # yarn lockfile v1 -# bun ./bun.lockb --hash: 59EFAFF863E05802-49a1b3c60ad8086c-97822B28B96561D6-fe1a69921f5effa8 +# bun ./bun.lockb --hash: 49148BDBE7A5ED81-8d4a9d95364a6ab6-B15901F22E8AD9D2-47492fb1a38fda2e "@ai-sdk/anthropic@1.1.6": @@ -10479,7 +10479,7 @@ buffer-from@^1.0.0: unsplash-js "7.0.18" use-debounce "9.0.4" zod "3.23.8" - zod-validation-error "3.0.3" + zod-validation-error "3.4.0" zustand "4.5.0" bun-types@1.2.2: @@ -22996,10 +22996,10 @@ zod-to-json-schema@^3.20.3, zod-to-json-schema@^3.24.1: resolved "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.1.tgz" integrity sha512-3h08nf3Vw3Wl3PK+q3ow/lIil81IT2Oa7YpQyUUDsEWbXveMesdfK1xBd2RhCkynwZndAxixji/7SYJJowr62w== -zod-validation-error@3.0.3: - version "3.0.3" - resolved "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-3.0.3.tgz" - integrity sha512-cETTrcMq3Ze58vhdR0zD37uJm/694I6mAxcf/ei5bl89cC++fBNxrC2z8lkFze/8hVMPwrbtrwXHR2LB50fpHw== +zod-validation-error@3.4.0: + version "3.4.0" + resolved "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-3.4.0.tgz" + integrity sha512-ZOPR9SVY6Pb2qqO5XHt+MkkTRxGXb4EVtnjc9JpXUOtUB1T9Ru7mZOT361AN3MsetVe7R0a1KZshJDZdgp9miQ== zustand@4.5.0, zustand@>=4.3.9, zustand@^4.4.7: version "4.5.0"