From a32b213ca5d00e33050baf41a8eab9c37d44daac Mon Sep 17 00:00:00 2001 From: victorvhs017 <115753265+victorvhs017@users.noreply.github.com> Date: Mon, 21 Apr 2025 19:41:54 +0700 Subject: [PATCH] chore: Enable Sentry integration (#5337) Co-authored-by: Dhruwang --- .env.example | 5 ++ apps/web/.gitignore | 2 +- apps/web/app/error.test.tsx | 72 +++++++++++++++++++ apps/web/app/error.tsx | 7 +- apps/web/app/global-error.test.tsx | 41 +++++++++++ apps/web/app/global-error.tsx | 22 ++++++ apps/web/app/layout.tsx | 4 +- apps/web/app/sentry/SentryProvider.test.tsx | 23 ++++-- apps/web/app/sentry/SentryProvider.tsx | 5 +- apps/web/instrumentation.ts | 9 ++- .../modules/api/v2/lib/tests/utils.test.ts | 51 +++++++++++++ apps/web/modules/api/v2/lib/utils.ts | 22 +++++- .../api-keys/components/add-api-key-modal.tsx | 9 ++- apps/web/next.config.mjs | 42 ++++------- apps/web/sentry.edge.config.ts | 5 +- apps/web/sentry.server.config.ts | 5 +- apps/web/tsconfig.json | 1 + apps/web/vite.config.mts | 2 + docker/docker-compose.yml | 10 ++- .../configuration/environment-variables.mdx | 4 +- packages/lib/messages/de-DE.json | 1 + packages/lib/messages/en-US.json | 1 + packages/lib/messages/fr-FR.json | 1 + packages/lib/messages/pt-BR.json | 1 + packages/lib/messages/pt-PT.json | 1 + packages/lib/messages/zh-Hant-TW.json | 1 + packages/lib/tsconfig.json | 1 + pnpm-lock.yaml | 2 +- 28 files changed, 294 insertions(+), 56 deletions(-) create mode 100644 apps/web/app/error.test.tsx create mode 100644 apps/web/app/global-error.test.tsx create mode 100644 apps/web/app/global-error.tsx diff --git a/.env.example b/.env.example index 047f0cfc5e..30cad4483c 100644 --- a/.env.example +++ b/.env.example @@ -219,3 +219,8 @@ UNKEY_ROOT_KEY= # PROMETHEUS_ENABLED= # PROMETHEUS_EXPORTER_PORT= +# The SENTRY_DSN is used for error tracking and performance monitoring with Sentry. +# SENTRY_DSN= +# The SENTRY_AUTH_TOKEN variable is picked up by the Sentry Build Plugin. +# It's used automatically by Sentry during the build for authentication when uploading source maps. +# SENTRY_AUTH_TOKEN= diff --git a/apps/web/.gitignore b/apps/web/.gitignore index 09190cb1b2..8a8922548e 100644 --- a/apps/web/.gitignore +++ b/apps/web/.gitignore @@ -50,4 +50,4 @@ uploads/ .sentryclirc # SAML Preloaded Connections -saml-connection/ \ No newline at end of file +saml-connection/ diff --git a/apps/web/app/error.test.tsx b/apps/web/app/error.test.tsx new file mode 100644 index 0000000000..b2c91817ab --- /dev/null +++ b/apps/web/app/error.test.tsx @@ -0,0 +1,72 @@ +import * as Sentry from "@sentry/nextjs"; +import { cleanup, render, screen, waitFor } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { afterEach, describe, expect, test, vi } from "vitest"; +import ErrorBoundary from "./error"; + +vi.mock("@/modules/ui/components/button", () => ({ + Button: (props: any) => , +})); + +vi.mock("@/modules/ui/components/error-component", () => ({ + ErrorComponent: () =>
ErrorComponent
, +})); + +vi.mock("@sentry/nextjs", () => ({ + captureException: vi.fn(), +})); + +describe("ErrorBoundary", () => { + afterEach(() => { + cleanup(); + }); + + const dummyError = new Error("Test error"); + const resetMock = vi.fn(); + + test("logs error via console.error in development", async () => { + (process.env as { [key: string]: string }).NODE_ENV = "development"; + const consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(() => {}); + vi.mocked(Sentry.captureException).mockImplementation((() => {}) as any); + + render(); + + await waitFor(() => { + expect(consoleErrorSpy).toHaveBeenCalledWith("Test error"); + }); + expect(Sentry.captureException).not.toHaveBeenCalled(); + }); + + test("captures error with Sentry in production", async () => { + (process.env as { [key: string]: string }).NODE_ENV = "production"; + const consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(() => {}); + vi.mocked(Sentry.captureException).mockImplementation((() => {}) as any); + + render(); + + await waitFor(() => { + expect(Sentry.captureException).toHaveBeenCalled(); + }); + expect(consoleErrorSpy).not.toHaveBeenCalled(); + }); + + test("calls reset when try again button is clicked", async () => { + render(); + const tryAgainBtn = screen.getByRole("button", { name: "common.try_again" }); + userEvent.click(tryAgainBtn); + await waitFor(() => expect(resetMock).toHaveBeenCalled()); + }); + + test("sets window.location.href to '/' when dashboard button is clicked", async () => { + const originalLocation = window.location; + delete (window as any).location; + (window as any).location = { href: "" }; + render(); + const dashBtn = screen.getByRole("button", { name: "common.go_to_dashboard" }); + userEvent.click(dashBtn); + await waitFor(() => { + expect(window.location.href).toBe("/"); + }); + window.location = originalLocation; + }); +}); diff --git a/apps/web/app/error.tsx b/apps/web/app/error.tsx index a99389a6a0..b16482cd7e 100644 --- a/apps/web/app/error.tsx +++ b/apps/web/app/error.tsx @@ -3,12 +3,15 @@ // Error components must be Client components import { Button } from "@/modules/ui/components/button"; import { ErrorComponent } from "@/modules/ui/components/error-component"; +import * as Sentry from "@sentry/nextjs"; import { useTranslate } from "@tolgee/react"; -const Error = ({ error, reset }: { error: Error; reset: () => void }) => { +const ErrorBoundary = ({ error, reset }: { error: Error; reset: () => void }) => { const { t } = useTranslate(); if (process.env.NODE_ENV === "development") { console.error(error.message); + } else { + Sentry.captureException(error); } return ( @@ -24,4 +27,4 @@ const Error = ({ error, reset }: { error: Error; reset: () => void }) => { ); }; -export default Error; +export default ErrorBoundary; diff --git a/apps/web/app/global-error.test.tsx b/apps/web/app/global-error.test.tsx new file mode 100644 index 0000000000..52b339d031 --- /dev/null +++ b/apps/web/app/global-error.test.tsx @@ -0,0 +1,41 @@ +import * as Sentry from "@sentry/nextjs"; +import { cleanup, render, waitFor } from "@testing-library/react"; +import { afterEach, describe, expect, test, vi } from "vitest"; +import GlobalError from "./global-error"; + +vi.mock("@sentry/nextjs", () => ({ + captureException: vi.fn(), +})); + +describe("GlobalError", () => { + const dummyError = new Error("Test error"); + + afterEach(() => { + cleanup(); + }); + + test("logs error using console.error in development", async () => { + (process.env as { [key: string]: string }).NODE_ENV = "development"; + const consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(() => {}); + vi.mocked(Sentry.captureException).mockImplementation((() => {}) as any); + + render(); + + await waitFor(() => { + expect(consoleErrorSpy).toHaveBeenCalledWith("Test error"); + }); + expect(Sentry.captureException).not.toHaveBeenCalled(); + }); + + test("captures error with Sentry in production", async () => { + (process.env as { [key: string]: string }).NODE_ENV = "production"; + const consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(() => {}); + + render(); + + await waitFor(() => { + expect(Sentry.captureException).toHaveBeenCalled(); + }); + expect(consoleErrorSpy).not.toHaveBeenCalled(); + }); +}); diff --git a/apps/web/app/global-error.tsx b/apps/web/app/global-error.tsx new file mode 100644 index 0000000000..077670f229 --- /dev/null +++ b/apps/web/app/global-error.tsx @@ -0,0 +1,22 @@ +"use client"; + +import * as Sentry from "@sentry/nextjs"; +import NextError from "next/error"; +import { useEffect } from "react"; + +export default function GlobalError({ error }: { error: Error & { digest?: string } }) { + useEffect(() => { + if (process.env.NODE_ENV === "development") { + console.error(error.message); + } else { + Sentry.captureException(error); + } + }, [error]); + return ( + + + + + + ); +} diff --git a/apps/web/app/layout.tsx b/apps/web/app/layout.tsx index 6b541b9ddc..d013bfe5d0 100644 --- a/apps/web/app/layout.tsx +++ b/apps/web/app/layout.tsx @@ -5,7 +5,7 @@ import { getTolgee } from "@/tolgee/server"; import { TolgeeStaticData } from "@tolgee/react"; import { Metadata } from "next"; import React from "react"; -import { SENTRY_DSN } from "@formbricks/lib/constants"; +import { IS_PRODUCTION, SENTRY_DSN } from "@formbricks/lib/constants"; import "../modules/ui/globals.css"; export const metadata: Metadata = { @@ -25,7 +25,7 @@ const RootLayout = async ({ children }: { children: React.ReactNode }) => { return ( - + {children} diff --git a/apps/web/app/sentry/SentryProvider.test.tsx b/apps/web/app/sentry/SentryProvider.test.tsx index f0ea92a832..70c66b793e 100644 --- a/apps/web/app/sentry/SentryProvider.test.tsx +++ b/apps/web/app/sentry/SentryProvider.test.tsx @@ -17,17 +17,18 @@ vi.mock("@sentry/nextjs", async () => { }; }); +const sentryDsn = "https://examplePublicKey@o0.ingest.sentry.io/0"; + describe("SentryProvider", () => { afterEach(() => { cleanup(); }); test("calls Sentry.init when sentryDsn is provided", () => { - const sentryDsn = "https://examplePublicKey@o0.ingest.sentry.io/0"; const initSpy = vi.spyOn(Sentry, "init").mockImplementation(() => undefined); render( - +
Test Content
); @@ -59,22 +60,32 @@ describe("SentryProvider", () => { expect(initSpy).not.toHaveBeenCalled(); }); - test("renders children", () => { - const sentryDsn = "https://examplePublicKey@o0.ingest.sentry.io/0"; + test("does not call Sentry.init when isEnabled is not provided", () => { + const initSpy = vi.spyOn(Sentry, "init").mockImplementation(() => undefined); + render(
Test Content
); + + expect(initSpy).not.toHaveBeenCalled(); + }); + + test("renders children", () => { + render( + +
Test Content
+
+ ); expect(screen.getByTestId("child")).toHaveTextContent("Test Content"); }); test("processes beforeSend correctly", () => { - const sentryDsn = "https://examplePublicKey@o0.ingest.sentry.io/0"; const initSpy = vi.spyOn(Sentry, "init").mockImplementation(() => undefined); render( - +
Test Content
); diff --git a/apps/web/app/sentry/SentryProvider.tsx b/apps/web/app/sentry/SentryProvider.tsx index cdaf6bc1ac..beb2d6c06f 100644 --- a/apps/web/app/sentry/SentryProvider.tsx +++ b/apps/web/app/sentry/SentryProvider.tsx @@ -6,11 +6,12 @@ import { useEffect } from "react"; interface SentryProviderProps { children: React.ReactNode; sentryDsn?: string; + isEnabled?: boolean; } -export const SentryProvider = ({ children, sentryDsn }: SentryProviderProps) => { +export const SentryProvider = ({ children, sentryDsn, isEnabled }: SentryProviderProps) => { useEffect(() => { - if (sentryDsn) { + if (sentryDsn && isEnabled) { Sentry.init({ dsn: sentryDsn, diff --git a/apps/web/instrumentation.ts b/apps/web/instrumentation.ts index e86284efd3..c7783584a0 100644 --- a/apps/web/instrumentation.ts +++ b/apps/web/instrumentation.ts @@ -1,14 +1,17 @@ -import { PROMETHEUS_ENABLED, SENTRY_DSN } from "@formbricks/lib/constants"; +import * as Sentry from "@sentry/nextjs"; +import { IS_PRODUCTION, PROMETHEUS_ENABLED, SENTRY_DSN } from "@formbricks/lib/constants"; + +export const onRequestError = Sentry.captureRequestError; // instrumentation.ts export const register = async () => { if (process.env.NEXT_RUNTIME === "nodejs" && PROMETHEUS_ENABLED) { await import("./instrumentation-node"); } - if (process.env.NEXT_RUNTIME === "nodejs" && SENTRY_DSN) { + if (process.env.NEXT_RUNTIME === "nodejs" && IS_PRODUCTION && SENTRY_DSN) { await import("./sentry.server.config"); } - if (process.env.NEXT_RUNTIME === "edge" && SENTRY_DSN) { + if (process.env.NEXT_RUNTIME === "edge" && IS_PRODUCTION && SENTRY_DSN) { await import("./sentry.edge.config"); } }; diff --git a/apps/web/modules/api/v2/lib/tests/utils.test.ts b/apps/web/modules/api/v2/lib/tests/utils.test.ts index 0885a565cd..00ea4d2880 100644 --- a/apps/web/modules/api/v2/lib/tests/utils.test.ts +++ b/apps/web/modules/api/v2/lib/tests/utils.test.ts @@ -1,4 +1,5 @@ import { ApiErrorResponseV2 } from "@/modules/api/v2/types/api-error"; +import * as Sentry from "@sentry/nextjs"; import { describe, expect, test, vi } from "vitest"; import { ZodError } from "zod"; import { logger } from "@formbricks/logger"; @@ -9,6 +10,16 @@ const mockRequest = new Request("http://localhost"); // Add the request id header mockRequest.headers.set("x-request-id", "123"); +vi.mock("@sentry/nextjs", () => ({ + captureException: vi.fn(), +})); + +// Mock SENTRY_DSN constant +vi.mock("@formbricks/lib/constants", () => ({ + SENTRY_DSN: "mocked-sentry-dsn", + IS_PRODUCTION: true, +})); + describe("utils", () => { describe("handleApiError", () => { test('return bad request response for "bad_request" error', async () => { @@ -257,5 +268,45 @@ describe("utils", () => { // Restore the original method logger.withContext = originalWithContext; }); + + test("log API error details with SENTRY_DSN set", () => { + // Mock the withContext method and its returned error method + const errorMock = vi.fn(); + const withContextMock = vi.fn().mockReturnValue({ + error: errorMock, + }); + + // Mock Sentry's captureException method + vi.mocked(Sentry.captureException).mockImplementation((() => {}) as any); + + // Replace the original withContext with our mock + const originalWithContext = logger.withContext; + logger.withContext = withContextMock; + + const mockRequest = new Request("http://localhost/api/test"); + mockRequest.headers.set("x-request-id", "123"); + + const error: ApiErrorResponseV2 = { + type: "internal_server_error", + details: [{ field: "server", issue: "error occurred" }], + }; + + logApiError(mockRequest, error); + + // Verify withContext was called with the expected context + expect(withContextMock).toHaveBeenCalledWith({ + correlationId: "123", + error, + }); + + // Verify error was called on the child logger + expect(errorMock).toHaveBeenCalledWith("API Error Details"); + + // Verify Sentry.captureException was called + expect(Sentry.captureException).toHaveBeenCalled(); + + // Restore the original method + logger.withContext = originalWithContext; + }); }); }); diff --git a/apps/web/modules/api/v2/lib/utils.ts b/apps/web/modules/api/v2/lib/utils.ts index 845e22a7b6..ef5d77f307 100644 --- a/apps/web/modules/api/v2/lib/utils.ts +++ b/apps/web/modules/api/v2/lib/utils.ts @@ -1,6 +1,10 @@ +// @ts-nocheck // We can remove this when we update the prisma client and the typescript version +// if we don't add this we get build errors with prisma due to type-nesting import { responses } from "@/modules/api/v2/lib/response"; import { ApiErrorResponseV2 } from "@/modules/api/v2/types/api-error"; +import * as Sentry from "@sentry/nextjs"; import { ZodCustomIssue, ZodIssue } from "zod"; +import { IS_PRODUCTION, SENTRY_DSN } from "@formbricks/lib/constants"; import { logger } from "@formbricks/logger"; export const handleApiError = (request: Request, err: ApiErrorResponseV2): Response => { @@ -59,7 +63,6 @@ export const logApiRequest = (request: Request, responseStatus: number): void => Object.entries(queryParams).filter(([key]) => !sensitiveParams.includes(key.toLowerCase())) ); - // Info: Conveys general, operational messages about system progress and state. logger .withContext({ method, @@ -73,7 +76,22 @@ export const logApiRequest = (request: Request, responseStatus: number): void => }; export const logApiError = (request: Request, error: ApiErrorResponseV2): void => { - const correlationId = request.headers.get("x-request-id") || ""; + const correlationId = request.headers.get("x-request-id") ?? ""; + + // Send the error to Sentry if the DSN is set and the error type is internal_server_error + // This is useful for tracking down issues without overloading Sentry with errors + if (SENTRY_DSN && IS_PRODUCTION && error.type === "internal_server_error") { + const err = new Error(`API V2 error, id: ${correlationId}`); + + Sentry.captureException(err, { + extra: { + details: error.details, + type: error.type, + correlationId, + }, + }); + } + logger .withContext({ correlationId, diff --git a/apps/web/modules/organization/settings/api-keys/components/add-api-key-modal.tsx b/apps/web/modules/organization/settings/api-keys/components/add-api-key-modal.tsx index 16b679c68b..7afe72b872 100644 --- a/apps/web/modules/organization/settings/api-keys/components/add-api-key-modal.tsx +++ b/apps/web/modules/organization/settings/api-keys/components/add-api-key-modal.tsx @@ -350,8 +350,13 @@ export const AddApiKeyModal = ({ -
- +
+
+ +

+ {t("environments.project.api_keys.organization_access_description")} +

+
diff --git a/apps/web/next.config.mjs b/apps/web/next.config.mjs index d5e1abf85e..43a5a54a71 100644 --- a/apps/web/next.config.mjs +++ b/apps/web/next.config.mjs @@ -300,36 +300,22 @@ nextConfig.images.remotePatterns.push({ }); const sentryOptions = { - // For all available options, see: - // https://github.com/getsentry/sentry-webpack-plugin#options +// For all available options, see: +// https://www.npmjs.com/package/@sentry/webpack-plugin#options - // Suppresses source map uploading logs during build - silent: true, +org: "formbricks", +project: "formbricks-cloud", - org: "formbricks", - project: "formbricks-cloud", +// Only print logs for uploading source maps in CI +silent: true, + +// Upload a larger set of source maps for prettier stack traces (increases build time) +widenClientFileUpload: true, + +// Automatically tree-shake Sentry logger statements to reduce bundle size +disableLogger: true, }; -const sentryConfig = { - // For all available options, see: - // https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/ +const exportConfig = (process.env.SENTRY_DSN && process.env.NODE_ENV === "production") ? withSentryConfig(nextConfig, sentryOptions) : nextConfig; - // Upload a larger set of source maps for prettier stack traces (increases build time) - widenClientFileUpload: true, - - // Transpiles SDK to be compatible with IE11 (increases bundle size) - transpileClientSDK: true, - - // Routes browser requests to Sentry through a Next.js rewrite to circumvent ad-blockers (increases server load) - tunnelRoute: "/monitoring", - - // Hides source maps from generated client bundles - hideSourceMaps: true, - - // Automatically tree-shake Sentry logger statements to reduce bundle size - disableLogger: true, -}; - -const exportConfig = process.env.SENTRY_DSN ? withSentryConfig(nextConfig, sentryOptions) : nextConfig; - -export default nextConfig; +export default exportConfig; diff --git a/apps/web/sentry.edge.config.ts b/apps/web/sentry.edge.config.ts index c882e14790..a63f39d14a 100644 --- a/apps/web/sentry.edge.config.ts +++ b/apps/web/sentry.edge.config.ts @@ -4,9 +4,10 @@ // https://docs.sentry.io/platforms/javascript/guides/nextjs/ import * as Sentry from "@sentry/nextjs"; import { SENTRY_DSN } from "@formbricks/lib/constants"; +import { logger } from "@formbricks/logger"; if (SENTRY_DSN) { - console.log("Sentry DSN found, enabling Sentry on the edge"); + logger.info("Sentry DSN found, enabling Sentry on the edge"); Sentry.init({ dsn: SENTRY_DSN, @@ -18,5 +19,5 @@ if (SENTRY_DSN) { debug: false, }); } else { - console.warn("Sentry DSN not found, Sentry will be disabled on the edge"); + logger.warn("Sentry DSN not found, Sentry will be disabled on the edge"); } diff --git a/apps/web/sentry.server.config.ts b/apps/web/sentry.server.config.ts index c7a59cf053..f237f168ee 100644 --- a/apps/web/sentry.server.config.ts +++ b/apps/web/sentry.server.config.ts @@ -3,9 +3,10 @@ // https://docs.sentry.io/platforms/javascript/guides/nextjs/ import * as Sentry from "@sentry/nextjs"; import { SENTRY_DSN } from "@formbricks/lib/constants"; +import { logger } from "@formbricks/logger"; if (SENTRY_DSN) { - console.log("Sentry DSN found, enabling Sentry on the server"); + logger.info("Sentry DSN found, enabling Sentry on the server"); Sentry.init({ dsn: SENTRY_DSN, @@ -31,5 +32,5 @@ if (SENTRY_DSN) { }, }); } else { - console.warn("Sentry DSN not found, Sentry will be disabled on the server"); + logger.warn("Sentry DSN not found, Sentry will be disabled on the server"); } diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json index b553ecd923..dfdc2e93df 100644 --- a/apps/web/tsconfig.json +++ b/apps/web/tsconfig.json @@ -12,6 +12,7 @@ "name": "next" } ], + "skipLibCheck": true, "strictNullChecks": true }, "exclude": ["../../.env", "node_modules"], diff --git a/apps/web/vite.config.mts b/apps/web/vite.config.mts index 8b3f2a273c..f4b7720383 100644 --- a/apps/web/vite.config.mts +++ b/apps/web/vite.config.mts @@ -67,6 +67,8 @@ export default defineConfig({ "modules/ee/contacts/segments/components/segment-settings.tsx", "modules/ee/contacts/api/v2/management/contacts/bulk/lib/contact.ts", "modules/ee/sso/components/**/*.tsx", + "app/global-error.tsx", + "app/error.tsx", "modules/account/**/*.tsx", "modules/account/**/*.ts", "modules/analysis/**/*.tsx", diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 02a5af7296..4d91b5e0b5 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -79,8 +79,14 @@ x-environment: &environment # SURVEY_URL: # Configure Formbricks usage within Formbricks. - #FORMBRICKS_API_HOST: - #FORMBRICKS_ENVIRONMENT_ID: + # FORMBRICKS_API_HOST: + # FORMBRICKS_ENVIRONMENT_ID: + + # The SENTRY_DSN is used for error tracking and performance monitoring with Sentry. + # SENTRY_DSN: + # It's used for authentication when uploading source maps to Sentry, to make errors more readable. + # SENTRY_AUTH_TOKEN: + ################################################### OPTIONAL (STORAGE) ################################################### diff --git a/docs/self-hosting/configuration/environment-variables.mdx b/docs/self-hosting/configuration/environment-variables.mdx index 8e2d8693ba..5fcc992e3a 100644 --- a/docs/self-hosting/configuration/environment-variables.mdx +++ b/docs/self-hosting/configuration/environment-variables.mdx @@ -65,6 +65,8 @@ These variables are present inside your machine’s docker-compose file. Restart | PROMETHEUS_ENABLED | Enables Prometheus metrics if set to 1. | optional | | | PROMETHEUS_EXPORTER_PORT | Port for Prometheus metrics. | optional | 9090 | | DOCKER_CRON_ENABLED | Controls whether cron jobs run in the Docker image. Set to 0 to disable (useful for cluster setups). | optional | 1 | -| SURVEY_URL | Set this to change the domain of the survey. | optional | WEBAPP_URL | +| SURVEY_URL | Set this to change the domain of the survey. | optional | WEBAPP_URL +| SENTRY_DSN | Set this to track errors and monitor performance in Sentry. | optional | +| SENTRY_AUTH_TOKEN | Set this if you want to make errors more readable in Sentry. | optional | Note: If you want to configure something that is not possible via above, please open an issue on our GitHub repo here or reach out to us on Github Discussions and we’ll try our best to work out a solution with you. diff --git a/packages/lib/messages/de-DE.json b/packages/lib/messages/de-DE.json index 6930448e1c..bf4266608a 100644 --- a/packages/lib/messages/de-DE.json +++ b/packages/lib/messages/de-DE.json @@ -789,6 +789,7 @@ "no_api_keys_yet": "Du hast noch keine API-Schlüssel", "no_env_permissions_found": "Keine Umgebungsberechtigungen gefunden", "organization_access": "Organisationszugang", + "organization_access_description": "Wähle Lese- oder Schreibrechte für organisationsweite Ressourcen aus.", "permissions": "Berechtigungen", "project_access": "Projektzugriff", "secret": "Geheimnis", diff --git a/packages/lib/messages/en-US.json b/packages/lib/messages/en-US.json index ffeefe4544..9fd2ab1c00 100644 --- a/packages/lib/messages/en-US.json +++ b/packages/lib/messages/en-US.json @@ -789,6 +789,7 @@ "no_api_keys_yet": "You don't have any API keys yet", "no_env_permissions_found": "No environment permissions found", "organization_access": "Organization Access", + "organization_access_description": "Select read or write privileges for organization-wide resources.", "permissions": "Permissions", "project_access": "Project Access", "secret": "Secret", diff --git a/packages/lib/messages/fr-FR.json b/packages/lib/messages/fr-FR.json index a7b915892d..3c3847d8e3 100644 --- a/packages/lib/messages/fr-FR.json +++ b/packages/lib/messages/fr-FR.json @@ -789,6 +789,7 @@ "no_api_keys_yet": "Vous n'avez pas encore de clés API.", "no_env_permissions_found": "Aucune autorisation d'environnement trouvée", "organization_access": "Accès à l'organisation", + "organization_access_description": "Sélectionnez les privilèges de lecture ou d'écriture pour les ressources de l'organisation.", "permissions": "Permissions", "project_access": "Accès au projet", "secret": "Secret", diff --git a/packages/lib/messages/pt-BR.json b/packages/lib/messages/pt-BR.json index ecc6728bf5..a63de71292 100644 --- a/packages/lib/messages/pt-BR.json +++ b/packages/lib/messages/pt-BR.json @@ -789,6 +789,7 @@ "no_api_keys_yet": "Você ainda não tem nenhuma chave de API", "no_env_permissions_found": "Nenhuma permissão de ambiente encontrada", "organization_access": "Acesso à Organização", + "organization_access_description": "Selecione privilégios de leitura ou escrita para recursos de toda a organização.", "permissions": "Permissões", "project_access": "Acesso ao Projeto", "secret": "Segredo", diff --git a/packages/lib/messages/pt-PT.json b/packages/lib/messages/pt-PT.json index 9930e9de90..ed6a9bf4a7 100644 --- a/packages/lib/messages/pt-PT.json +++ b/packages/lib/messages/pt-PT.json @@ -789,6 +789,7 @@ "no_api_keys_yet": "Ainda não tem nenhuma chave API", "no_env_permissions_found": "Nenhuma permissão de ambiente encontrada", "organization_access": "Acesso à Organização", + "organization_access_description": "Selecione privilégios de leitura ou escrita para recursos de toda a organização.", "permissions": "Permissões", "project_access": "Acesso ao Projeto", "secret": "Segredo", diff --git a/packages/lib/messages/zh-Hant-TW.json b/packages/lib/messages/zh-Hant-TW.json index 6a6f694875..ca06272bd5 100644 --- a/packages/lib/messages/zh-Hant-TW.json +++ b/packages/lib/messages/zh-Hant-TW.json @@ -789,6 +789,7 @@ "no_api_keys_yet": "您還沒有任何 API 金鑰", "no_env_permissions_found": "找不到環境權限", "organization_access": "組織 Access", + "organization_access_description": "選擇組織範圍資源的讀取或寫入權限。", "permissions": "權限", "project_access": "專案存取", "secret": "密碼", diff --git a/packages/lib/tsconfig.json b/packages/lib/tsconfig.json index 4ee33b2934..5dd405c66d 100644 --- a/packages/lib/tsconfig.json +++ b/packages/lib/tsconfig.json @@ -10,6 +10,7 @@ "name": "next" } ], + "skipLibCheck": true, "strictNullChecks": true }, "exclude": ["dist", "build", "node_modules", "../../packages/types/surveys.d.ts"], diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f1b72e1f62..3502df8f65 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -21319,7 +21319,7 @@ snapshots: dependencies: ansi-align: 3.0.1 camelcase: 7.0.1 - chalk: 5.0.1 + chalk: 5.4.1 cli-boxes: 3.0.0 string-width: 5.1.2 type-fest: 2.19.0