diff --git a/packages/web/.gitignore b/packages/web/.gitignore index fa49c114b..681e08644 100644 --- a/packages/web/.gitignore +++ b/packages/web/.gitignore @@ -37,3 +37,6 @@ yarn-error.log* /storybook-static/ # Sentry .sentryclirc + +# Sentry Config File +.env.sentry-build-plugin diff --git a/packages/web/instrumentation.ts b/packages/web/instrumentation.ts new file mode 100644 index 000000000..39025fe12 --- /dev/null +++ b/packages/web/instrumentation.ts @@ -0,0 +1,5 @@ +export async function register() { + if (process.env.NEXT_RUNTIME === 'nodejs') { + await import('./sentry.server.config') + } +} diff --git a/packages/web/next.config.js b/packages/web/next.config.js index 637f1bd05..4b2d22917 100644 --- a/packages/web/next.config.js +++ b/packages/web/next.config.js @@ -1,13 +1,13 @@ const ContentSecurityPolicy = ` default-src 'self'; base-uri 'self'; - connect-src 'self' ${process.env.NEXT_PUBLIC_SERVER_BASE_URL} https://proxy-prod.omnivore-image-cache.app https://accounts.google.com https://proxy-demo.omnivore-image-cache.app https://storage.googleapis.com https://widget.intercom.io https://api-iam.intercom.io https://static.intercomassets.com https://downloads.intercomcdn.com https://platform.twitter.com wss://nexus-websocket-a.intercom.io wss://nexus-websocket-b.intercom.io wss://nexus-europe-websocket.intercom.io wss://nexus-australia-websocket.intercom.io https://uploads.intercomcdn.com https://tools.applemediaservices.com wss://www.tiktok.com; + connect-src 'self' ${process.env.NEXT_PUBLIC_SERVER_BASE_URL} https://proxy-prod.omnivore-image-cache.app https://accounts.google.com https://proxy-demo.omnivore-image-cache.app https://storage.googleapis.com https://widget.intercom.io https://api-iam.intercom.io https://static.intercomassets.com https://downloads.intercomcdn.com https://platform.twitter.com wss://nexus-websocket-a.intercom.io wss://nexus-websocket-b.intercom.io wss://nexus-europe-websocket.intercom.io wss://nexus-australia-websocket.intercom.io https://uploads.intercomcdn.com https://tools.applemediaservices.com wss://www.tiktok.com *.sentry.io; font-src 'self' data: https://cdn.jsdelivr.net https://js.intercomcdn.com https://fonts.intercomcdn.com; form-action 'self' ${process.env.NEXT_PUBLIC_SERVER_BASE_URL} https://getpocket.com/auth/authorize https://intercom.help https://api-iam.intercom.io https://api-iam.eu.intercom.io https://api-iam.au.intercom.io https://www.notion.so https://api.notion.com; frame-ancestors 'none'; frame-src 'self' https://accounts.google.com https://platform.twitter.com https://www.youtube.com https://www.youtube-nocookie.com https://www.google.com/recaptcha/ https://recaptcha.google.com/recaptcha/ https://www.recaptcha.net https://www.tiktok.com; manifest-src 'self'; - script-src 'self' 'unsafe-inline' 'unsafe-eval' accounts.google.com https://widget.intercom.io https://js.intercomcdn.com https://platform.twitter.com https://cdnjs.cloudflare.com https://cdn.jsdelivr.net https://www.google.com/recaptcha/ https://www.gstatic.com/recaptcha/ https://www.recaptcha.net https://www.gstatic.cn/ https://*.neutral.ttwstatic.com https://www.tiktok.com/embed.js; + script-src 'self' 'unsafe-inline' 'unsafe-eval' accounts.google.com https://widget.intercom.io https://js.intercomcdn.com https://platform.twitter.com https://cdnjs.cloudflare.com https://cdn.jsdelivr.net https://www.google.com/recaptcha/ https://www.gstatic.com/recaptcha/ https://www.recaptcha.net https://www.gstatic.cn/ https://*.neutral.ttwstatic.com https://www.tiktok.com/embed.js https://browser.sentry-cdn.com https://js.sentry-cdn.com; style-src 'self' 'unsafe-inline' https://accounts.google.com https://cdnjs.cloudflare.com https://*.neutral.ttwstatic.com; img-src 'self' blob: data: https:; worker-src 'self' blob:; @@ -249,3 +249,42 @@ const withBundleAnalyzer = require('@next/bundle-analyzer')({ enabled: process.env.ANALYZE === 'true', }) module.exports = withBundleAnalyzer(moduleExports) + +// Injected content via Sentry wizard below + +const { withSentryConfig } = require('@sentry/nextjs') + +module.exports = withSentryConfig(module.exports, { + // For all available options, see: + // https://github.com/getsentry/sentry-webpack-plugin#options + + org: 'omnivore', + project: process.env.SENTRY_PROJECT, + + // Only print logs for uploading source maps in CI + silent: !process.env.CI, + + // For all available options, see: + // https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/ + + // Upload a larger set of source maps for prettier stack traces (increases build time) + widenClientFileUpload: true, + + // Route browser requests to Sentry through a Next.js rewrite to circumvent ad-blockers. + // This can increase your server load as well as your hosting bill. + // Note: Check that the configured route will not match with your Next.js middleware, otherwise reporting of client- + // side errors will fail. + tunnelRoute: '/monitoring', + + // Hides source maps from generated client bundles + hideSourceMaps: true, + + // Automatically tree-shake Sentry logger statements to reduce bundle size + disableLogger: true, + + // Enables automatic instrumentation of Vercel Cron Monitors. (Does not yet work with App Router route handlers.) + // See the following for more information: + // https://docs.sentry.io/product/crons/ + // https://vercel.com/docs/cron-jobs + automaticVercelMonitors: true, +}) diff --git a/packages/web/pages/_debug/sentry.tsx b/packages/web/pages/_debug/sentry.tsx new file mode 100644 index 000000000..1f6f8ad97 --- /dev/null +++ b/packages/web/pages/_debug/sentry.tsx @@ -0,0 +1,23 @@ +import { Button } from '../../components/elements/Button' +import { + BorderedFormInput, + FormLabel, +} from '../../components/elements/FormElements' +import { SpanBox, VStack } from '../../components/elements/LayoutPrimitives' +import { StyledText } from '../../components/elements/StyledText' +import { webBaseURL } from '../../lib/appConfig' + +export default function DebugShareTarget(): JSX.Element { + return ( + + ) +} diff --git a/packages/web/pages/_error.tsx b/packages/web/pages/_error.tsx index 967af76a8..746a65842 100644 --- a/packages/web/pages/_error.tsx +++ b/packages/web/pages/_error.tsx @@ -39,36 +39,7 @@ MyError.getInitialProps = async (context: any) => { return errorInitialProps } - // Running on the server, the response object (`res`) is available. - // - // Next.js will pass an err on the server if a page's data fetching methods - // threw or returned a Promise that rejected - // - // Running on the client (browser), Next.js will provide an err if: - // - // - a page's `getInitialProps` threw or returned a Promise that rejected - // - an exception was thrown somewhere in the React lifecycle (render, - // componentDidMount, etc) that was caught by Next.js's React Error - // Boundary. Read more about what types of exceptions are caught by Error - // Boundaries: https://reactjs.org/docs/error-boundaries.html - - if (err) { - Sentry.captureException(err) - - // Flushing before returning is necessary if deploying to Vercel, see - // https://vercel.com/docs/platform/limits#streaming-responses - await Sentry.flush(2000) - - return errorInitialProps - } - - // If this point is reached, getInitialProps was called without any - // information about what the error might be. This is unexpected and may - // indicate a bug introduced in Next.js, so record it in Sentry - Sentry.captureException( - new Error(`_error.js getInitialProps missing data at path: ${asPath}`) - ) - await Sentry.flush(2000) + await Sentry.captureUnderscoreErrorException(context) return errorInitialProps } diff --git a/packages/web/pages/api/client/auth.ts b/packages/web/pages/api/client/auth.ts index 06694c79a..8ad152b12 100644 --- a/packages/web/pages/api/client/auth.ts +++ b/packages/web/pages/api/client/auth.ts @@ -1,7 +1,6 @@ import type { NextApiRequest, NextApiResponse } from 'next' import { serialize } from 'cookie' import * as jwt from 'jsonwebtoken' -import { withSentry } from '@sentry/nextjs' import { ssoJwtSecret } from '../../../lib/appConfig' import { DEFAULT_HOME_PATH } from '../../../lib/navigations' @@ -37,4 +36,4 @@ const requestHandler = (req: NextApiRequest, res: NextApiResponse): void => { res.end() } -export default withSentry(requestHandler) +export default requestHandler diff --git a/packages/web/pages/api/client/logout.ts b/packages/web/pages/api/client/logout.ts index 58e2faadb..adc0c6da2 100644 --- a/packages/web/pages/api/client/logout.ts +++ b/packages/web/pages/api/client/logout.ts @@ -1,5 +1,4 @@ import type { NextApiRequest, NextApiResponse } from 'next' -import { withSentry } from '@sentry/nextjs' import { serialize } from 'cookie' const requestHandler = (req: NextApiRequest, res: NextApiResponse): void => { @@ -7,4 +6,4 @@ const requestHandler = (req: NextApiRequest, res: NextApiResponse): void => { res.send('logged out') } -export default withSentry(requestHandler) +export default requestHandler diff --git a/packages/web/sentry.client.config.ts b/packages/web/sentry.client.config.ts index 4516b4246..d27c438f0 100644 --- a/packages/web/sentry.client.config.ts +++ b/packages/web/sentry.client.config.ts @@ -1,15 +1,18 @@ +// This file configures the initialization of Sentry on the client. +// The config you add here will be used whenever a users loads a page in their browser. +// https://docs.sentry.io/platforms/javascript/guides/nextjs/ + import * as Sentry from '@sentry/nextjs' import { sentryDSN } from './lib/appConfig' if (sentryDSN) { Sentry.init({ dsn: sentryDSN, - // We recommend adjusting this value in production, or using tracesSampler - // for finer control - tracesSampleRate: 1.0, - // ... - // Note: if you want to override the automatic release value, do not set a - // `release` value here - use the environment variable `SENTRY_RELEASE`, so - // that it will also get attached to your source maps + + // Adjust this value in production, or use tracesSampler for greater control + tracesSampleRate: 1, + + // Setting this option to true will print useful information to the console while you're setting up Sentry. + debug: false, }) } diff --git a/packages/web/sentry.server.config.ts b/packages/web/sentry.server.config.ts index 4516b4246..b536abb9b 100644 --- a/packages/web/sentry.server.config.ts +++ b/packages/web/sentry.server.config.ts @@ -1,15 +1,21 @@ +// This file configures the initialization of Sentry on the server. +// The config you add here will be used whenever the server handles a request. +// https://docs.sentry.io/platforms/javascript/guides/nextjs/ + import * as Sentry from '@sentry/nextjs' import { sentryDSN } from './lib/appConfig' if (sentryDSN) { Sentry.init({ dsn: sentryDSN, - // We recommend adjusting this value in production, or using tracesSampler - // for finer control - tracesSampleRate: 1.0, - // ... - // Note: if you want to override the automatic release value, do not set a - // `release` value here - use the environment variable `SENTRY_RELEASE`, so - // that it will also get attached to your source maps + + // Adjust this value in production, or use tracesSampler for greater control + tracesSampleRate: 1, + + // Setting this option to true will print useful information to the console while you're setting up Sentry. + debug: false, + + // Uncomment the line below to enable Spotlight (https://spotlightjs.com) + // spotlight: process.env.NODE_ENV === 'development', }) }