Commit f6804087 authored by tom goriunov's avatar tom goriunov Committed by GitHub

refine Sentry setup (#1229)

* [skip ci] reusable workflow for publishing docker image

* [skip ci] clean up

* remove sentry/nextjs

* refine config and fetch error payload

* log 404

* make ENV variables for instance and enviroment optional

* add release and enviroment to csp
parent c4bcba60
import type { Feature } from './types'; import type { Feature } from './types';
import app from '../app';
import { getEnvValue } from '../utils'; import { getEnvValue } from '../utils';
const dsn = getEnvValue('NEXT_PUBLIC_SENTRY_DSN'); const dsn = getEnvValue('NEXT_PUBLIC_SENTRY_DSN');
const instance = (() => {
const envValue = getEnvValue('NEXT_PUBLIC_APP_INSTANCE');
if (envValue) {
return envValue;
}
return app.host?.replace('.blockscout.com', '').replaceAll('-', '_');
})();
const environment = getEnvValue('NEXT_PUBLIC_APP_ENV') || 'production';
const release = getEnvValue('NEXT_PUBLIC_GIT_TAG');
const cspReportUrl = (() => {
try {
const url = new URL(getEnvValue('SENTRY_CSP_REPORT_URI') || '');
// https://docs.sentry.io/product/security-policy-reporting/#additional-configuration
url.searchParams.set('sentry_environment', environment);
release && url.searchParams.set('sentry_release', release);
return url.toString();
} catch (error) {
return;
}
})();
const title = 'Sentry error monitoring'; const title = 'Sentry error monitoring';
const config: Feature<{ dsn: string; environment: string | undefined; cspReportUrl: string | undefined; instance: string | undefined }> = (() => { const config: Feature<{
if (dsn) { dsn: string;
cspReportUrl: string | undefined;
instance: string;
release: string | undefined;
environment: string;
}> = (() => {
if (dsn && instance && environment) {
return Object.freeze({ return Object.freeze({
title, title,
isEnabled: true, isEnabled: true,
dsn, dsn,
environment: getEnvValue('NEXT_PUBLIC_APP_ENV') || getEnvValue('NODE_ENV'), cspReportUrl,
cspReportUrl: getEnvValue('SENTRY_CSP_REPORT_URI'), instance,
instance: getEnvValue('NEXT_PUBLIC_APP_INSTANCE'), release,
environment,
}); });
} }
......
...@@ -40,7 +40,7 @@ const UI = Object.freeze({ ...@@ -40,7 +40,7 @@ const UI = Object.freeze({
isHidden: getEnvValue('NEXT_PUBLIC_HIDE_INDEXING_ALERT'), isHidden: getEnvValue('NEXT_PUBLIC_HIDE_INDEXING_ALERT'),
}, },
maintenanceAlert: { maintenanceAlert: {
message: getEnvValue(process.env.NEXT_PUBLIC_MAINTENANCE_ALERT_MESSAGE), message: getEnvValue('NEXT_PUBLIC_MAINTENANCE_ALERT_MESSAGE'),
}, },
explorers: { explorers: {
items: parseEnvJson<Array<NetworkExplorer>>(getEnvValue('NEXT_PUBLIC_NETWORK_EXPLORERS')) || [], items: parseEnvJson<Array<NetworkExplorer>>(getEnvValue('NEXT_PUBLIC_NETWORK_EXPLORERS')) || [],
......
...@@ -30,7 +30,6 @@ NEXT_PUBLIC_FEATURED_NETWORKS=https://raw.githubusercontent.com/blockscout/front ...@@ -30,7 +30,6 @@ NEXT_PUBLIC_FEATURED_NETWORKS=https://raw.githubusercontent.com/blockscout/front
NEXT_PUBLIC_NETWORK_EXPLORERS=[{'title':'Etherscan','baseUrl':'https://etherscan.io/','paths':{'tx':'/tx','address':'/address','token':'/token','block':'/block'}}] NEXT_PUBLIC_NETWORK_EXPLORERS=[{'title':'Etherscan','baseUrl':'https://etherscan.io/','paths':{'tx':'/tx','address':'/address','token':'/token','block':'/block'}}]
# app features # app features
NEXT_PUBLIC_APP_INSTANCE=local
NEXT_PUBLIC_APP_ENV=development NEXT_PUBLIC_APP_ENV=development
NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0xf7d4972356e6ae44ae948d0cf19ef2beaf0e574c180997e969a2837da15e349d NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0xf7d4972356e6ae44ae948d0cf19ef2beaf0e574c180997e969a2837da15e349d
NEXT_PUBLIC_HAS_BEACON_CHAIN=true NEXT_PUBLIC_HAS_BEACON_CHAIN=true
......
...@@ -33,7 +33,6 @@ NEXT_PUBLIC_NETWORK_ICON=https://raw.githubusercontent.com/blockscout/frontend-c ...@@ -33,7 +33,6 @@ NEXT_PUBLIC_NETWORK_ICON=https://raw.githubusercontent.com/blockscout/frontend-c
NEXT_PUBLIC_NETWORK_EXPLORERS=[{'title':'Bitquery','baseUrl':'https://explorer.bitquery.io/','paths':{'tx':'/goerli/tx','address':'/goerli/address','token':'/goerli/token','block':'/goerli/block'}},{'title':'Etherscan','baseUrl':'https://goerli.etherscan.io/','paths':{'tx':'/tx','address':'/address','token':'/token','block':'/block'}}] NEXT_PUBLIC_NETWORK_EXPLORERS=[{'title':'Bitquery','baseUrl':'https://explorer.bitquery.io/','paths':{'tx':'/goerli/tx','address':'/goerli/address','token':'/goerli/token','block':'/goerli/block'}},{'title':'Etherscan','baseUrl':'https://goerli.etherscan.io/','paths':{'tx':'/tx','address':'/address','token':'/token','block':'/block'}}]
# app features # app features
NEXT_PUBLIC_APP_INSTANCE=local
NEXT_PUBLIC_APP_ENV=development NEXT_PUBLIC_APP_ENV=development
NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0xf7d4972356e6ae44ae948d0cf19ef2beaf0e574c180997e969a2837da15e349d NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0xf7d4972356e6ae44ae948d0cf19ef2beaf0e574c180997e969a2837da15e349d
NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true
......
...@@ -33,7 +33,6 @@ NEXT_PUBLIC_FEATURED_NETWORKS=https://raw.githubusercontent.com/blockscout/front ...@@ -33,7 +33,6 @@ NEXT_PUBLIC_FEATURED_NETWORKS=https://raw.githubusercontent.com/blockscout/front
NEXT_PUBLIC_NETWORK_EXPLORERS=[{'title':'Anyblock','baseUrl':'https://explorer.anyblock.tools','paths':{'tx':'/ethereum/poa/core/transaction','address':'/ethereum/poa/core/address'}}] NEXT_PUBLIC_NETWORK_EXPLORERS=[{'title':'Anyblock','baseUrl':'https://explorer.anyblock.tools','paths':{'tx':'/ethereum/poa/core/transaction','address':'/ethereum/poa/core/address'}}]
# app features # app features
NEXT_PUBLIC_APP_INSTANCE=local
NEXT_PUBLIC_APP_ENV=development NEXT_PUBLIC_APP_ENV=development
NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true
NEXT_PUBLIC_AUTH_URL=http://localhost:3000 NEXT_PUBLIC_AUTH_URL=http://localhost:3000
......
...@@ -34,7 +34,6 @@ NEXT_PUBLIC_NETWORK_ICON=https://raw.githubusercontent.com/blockscout/frontend-c ...@@ -34,7 +34,6 @@ NEXT_PUBLIC_NETWORK_ICON=https://raw.githubusercontent.com/blockscout/frontend-c
NEXT_PUBLIC_NETWORK_EXPLORERS=[{'title':'Bitquery','baseUrl':'https://explorer.bitquery.io/','paths':{'tx':'/goerli/tx','address':'/goerli/address','token':'/goerli/token','block':'/goerli/block'}},{'title':'Etherscan','baseUrl':'https://goerli.etherscan.io/','paths':{'tx':'/tx','address':'/address','token':'/token','block':'/block'}}] NEXT_PUBLIC_NETWORK_EXPLORERS=[{'title':'Bitquery','baseUrl':'https://explorer.bitquery.io/','paths':{'tx':'/goerli/tx','address':'/goerli/address','token':'/goerli/token','block':'/goerli/block'}},{'title':'Etherscan','baseUrl':'https://goerli.etherscan.io/','paths':{'tx':'/tx','address':'/address','token':'/token','block':'/block'}}]
# app features # app features
NEXT_PUBLIC_APP_INSTANCE=local
NEXT_PUBLIC_APP_ENV=development NEXT_PUBLIC_APP_ENV=development
NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0xf7d4972356e6ae44ae948d0cf19ef2beaf0e574c180997e969a2837da15e349d NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0xf7d4972356e6ae44ae948d0cf19ef2beaf0e574c180997e969a2837da15e349d
NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true
......
...@@ -35,7 +35,6 @@ NEXT_PUBLIC_NETWORK_ICON=https://raw.githubusercontent.com/blockscout/frontend-c ...@@ -35,7 +35,6 @@ NEXT_PUBLIC_NETWORK_ICON=https://raw.githubusercontent.com/blockscout/frontend-c
NEXT_PUBLIC_VIEWS_ADDRESS_IDENTICON_TYPE=gradient_avatar NEXT_PUBLIC_VIEWS_ADDRESS_IDENTICON_TYPE=gradient_avatar
# app features # app features
NEXT_PUBLIC_APP_INSTANCE=local
NEXT_PUBLIC_APP_ENV=development NEXT_PUBLIC_APP_ENV=development
NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0x4a0ed8ddf751a7cb5297f827699117b0f6d21a0b2907594d300dc9fed75c7e62 NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0x4a0ed8ddf751a7cb5297f827699117b0f6d21a0b2907594d300dc9fed75c7e62
NEXT_PUBLIC_WEB3_WALLETS=['coinbase'] NEXT_PUBLIC_WEB3_WALLETS=['coinbase']
......
...@@ -32,7 +32,6 @@ NEXT_PUBLIC_NETWORK_ICON=https://raw.githubusercontent.com/blockscout/frontend-c ...@@ -32,7 +32,6 @@ NEXT_PUBLIC_NETWORK_ICON=https://raw.githubusercontent.com/blockscout/frontend-c
NEXT_PUBLIC_NETWORK_EXPLORERS=[{'title':'Anyblock','baseUrl':'https://explorer.anyblock.tools','paths':{'tx':'/ethereum/poa/core/transaction','address':'/ethereum/poa/core/address','block':'/ethereum/poa/core/block'}}] NEXT_PUBLIC_NETWORK_EXPLORERS=[{'title':'Anyblock','baseUrl':'https://explorer.anyblock.tools','paths':{'tx':'/ethereum/poa/core/transaction','address':'/ethereum/poa/core/address','block':'/ethereum/poa/core/block'}}]
# app features # app features
NEXT_PUBLIC_APP_INSTANCE=local
NEXT_PUBLIC_APP_ENV=development NEXT_PUBLIC_APP_ENV=development
NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true
NEXT_PUBLIC_AUTH_URL=http://localhost:3000 NEXT_PUBLIC_AUTH_URL=http://localhost:3000
......
...@@ -34,7 +34,6 @@ NEXT_PUBLIC_NETWORK_ICON=https://raw.githubusercontent.com/blockscout/frontend-c ...@@ -34,7 +34,6 @@ NEXT_PUBLIC_NETWORK_ICON=https://raw.githubusercontent.com/blockscout/frontend-c
## misc ## misc
# app features # app features
NEXT_PUBLIC_APP_INSTANCE=local
NEXT_PUBLIC_APP_ENV=development NEXT_PUBLIC_APP_ENV=development
# NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0x97fa753626b8d44011d0b9f9a947c735f20b6e895efdee49d7cda76a50001017 # NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0x97fa753626b8d44011d0b9f9a947c735f20b6e895efdee49d7cda76a50001017
NEXT_PUBLIC_HAS_BEACON_CHAIN=false NEXT_PUBLIC_HAS_BEACON_CHAIN=false
......
...@@ -35,7 +35,6 @@ NEXT_PUBLIC_VIEWS_BLOCK_HIDDEN_FIELDS=['burnt_fees','total_reward','nonce'] ...@@ -35,7 +35,6 @@ NEXT_PUBLIC_VIEWS_BLOCK_HIDDEN_FIELDS=['burnt_fees','total_reward','nonce']
## misc ## misc
# app features # app features
NEXT_PUBLIC_APP_INSTANCE=local
NEXT_PUBLIC_APP_ENV=development NEXT_PUBLIC_APP_ENV=development
NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0x97fa753626b8d44011d0b9f9a947c735f20b6e895efdee49d7cda76a50001017 NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0x97fa753626b8d44011d0b9f9a947c735f20b6e895efdee49d7cda76a50001017
NEXT_PUBLIC_HAS_BEACON_CHAIN=false NEXT_PUBLIC_HAS_BEACON_CHAIN=false
......
import type { NextjsOptions } from '@sentry/nextjs/types/utils/nextjsOptions';
const config: NextjsOptions = {
environment: process.env.NODE_ENV,
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
release: process.env.NEXT_PUBLIC_GIT_COMMIT_SHA,
// We recommend adjusting this value in production, or using tracesSampler
// for finer control
tracesSampleRate: 1.0,
// error filtering settings
// were taken from here - https://docs.sentry.io/platforms/node/guides/azure-functions/configuration/filtering/#decluttering-sentry
ignoreErrors: [
// Random plugins/extensions
'top.GLOBALS',
// See: http://blog.errorception.com/2012/03/tale-of-unfindable-js-error.html
'originalCreateNotification',
'canvas.contentDocument',
'MyApp_RemoveAllHighlights',
'http://tt.epicplay.com',
'Can\'t find variable: ZiteReader',
'jigsaw is not defined',
'ComboSearch is not defined',
'http://loading.retry.widdit.com/',
'atomicFindClose',
// Facebook borked
'fb_xd_fragment',
// ISP "optimizing" proxy - `Cache-Control: no-transform` seems to reduce this. (thanks @acdha)
// See http://stackoverflow.com/questions/4113268/how-to-stop-javascript-injection-from-vodafone-proxy
'bmi_SafeAddOnload',
'EBCallBackMessageReceived',
// See http://toolbar.conduit.com/Developer/HtmlAndGadget/Methods/JSInjection.aspx
'conduitPage',
// Generic error code from errors outside the security sandbox
'Script error.',
],
denyUrls: [
// Facebook flakiness
/graph\.facebook\.com/i,
// Facebook blocked
/connect\.facebook\.net\/en_US\/all\.js/i,
// Woopra flakiness
/eatdifferent\.com\.woopra-ns\.com/i,
/static\.woopra\.com\/js\/woopra\.js/i,
// Chrome extensions
/extensions\//i,
/^chrome:\/\//i,
// Other plugins
/127\.0\.0\.1:4001\/isrunning/i, // Cacaoweb
/webappstoolbarba\.texthelp\.com\//i,
/metrics\.itunes\.apple\.com\.edgesuite\.net\//i,
],
};
export default config;
...@@ -155,14 +155,14 @@ const sentrySchema = yup ...@@ -155,14 +155,14 @@ const sentrySchema = yup
.string() .string()
.when('NEXT_PUBLIC_SENTRY_DSN', { .when('NEXT_PUBLIC_SENTRY_DSN', {
is: (value: string) => Boolean(value), is: (value: string) => Boolean(value),
then: (schema) => schema.required(), then: (schema) => schema,
otherwise: (schema) => schema.max(-1, 'NEXT_PUBLIC_APP_INSTANCE cannot not be used without NEXT_PUBLIC_SENTRY_DSN'), otherwise: (schema) => schema.max(-1, 'NEXT_PUBLIC_APP_INSTANCE cannot not be used without NEXT_PUBLIC_SENTRY_DSN'),
}), }),
NEXT_PUBLIC_APP_ENV: yup NEXT_PUBLIC_APP_ENV: yup
.string() .string()
.when('NEXT_PUBLIC_SENTRY_DSN', { .when('NEXT_PUBLIC_SENTRY_DSN', {
is: (value: string) => Boolean(value), is: (value: string) => Boolean(value),
then: (schema) => schema.required(), then: (schema) => schema,
otherwise: (schema) => schema.max(-1, 'NEXT_PUBLIC_APP_ENV cannot not be used without NEXT_PUBLIC_SENTRY_DSN'), otherwise: (schema) => schema.max(-1, 'NEXT_PUBLIC_APP_ENV cannot not be used without NEXT_PUBLIC_SENTRY_DSN'),
}), }),
}); });
......
...@@ -174,8 +174,8 @@ frontend: ...@@ -174,8 +174,8 @@ frontend:
cpu: 200m cpu: 200m
memory: 256Mi memory: 256Mi
env: env:
NEXT_PUBLIC_APP_ENV: stable NEXT_PUBLIC_APP_ENV: development
NEXT_PUBLIC_APP_INSTANCE: base_goerli NEXT_PUBLIC_APP_INSTANCE: main_L2
NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE: validation NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE: validation
NEXT_PUBLIC_NETWORK_LOGO: https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/base.svg NEXT_PUBLIC_NETWORK_LOGO: https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/base.svg
NEXT_PUBLIC_NETWORK_ICON: https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/base.svg NEXT_PUBLIC_NETWORK_ICON: https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/base.svg
......
...@@ -146,8 +146,8 @@ frontend: ...@@ -146,8 +146,8 @@ frontend:
NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE: validation NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE: validation
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM: https://airtable.com/shrqUAcjgGJ4jU88C NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM: https://airtable.com/shrqUAcjgGJ4jU88C
NEXT_PUBLIC_APP_ENV: stable NEXT_PUBLIC_APP_ENV: development
NEXT_PUBLIC_APP_INSTANCE: eth_goerli NEXT_PUBLIC_APP_INSTANCE: main
NEXT_PUBLIC_STATS_API_HOST: https://stats-test.k8s-dev.blockscout.com/ NEXT_PUBLIC_STATS_API_HOST: https://stats-test.k8s-dev.blockscout.com/
NEXT_PUBLIC_VISUALIZE_API_HOST: http://visualizer-svc.visualizer-testing.svc.cluster.local/ NEXT_PUBLIC_VISUALIZE_API_HOST: http://visualizer-svc.visualizer-testing.svc.cluster.local/
NEXT_PUBLIC_CONTRACT_INFO_API_HOST: https://contracts-info-test.k8s-dev.blockscout.com NEXT_PUBLIC_CONTRACT_INFO_API_HOST: https://contracts-info-test.k8s-dev.blockscout.com
......
...@@ -63,9 +63,9 @@ frontend: ...@@ -63,9 +63,9 @@ frontend:
enabled: false enabled: false
environment: environment:
NEXT_PUBLIC_APP_ENV: NEXT_PUBLIC_APP_ENV:
_default: preview _default: development
NEXT_PUBLIC_APP_INSTANCE: NEXT_PUBLIC_APP_INSTANCE:
_default: base_goerli _default: review_L2
NEXT_PUBLIC_NETWORK_NAME: NEXT_PUBLIC_NETWORK_NAME:
_default: "Base Göerli" _default: "Base Göerli"
NEXT_PUBLIC_NETWORK_SHORT_NAME: NEXT_PUBLIC_NETWORK_SHORT_NAME:
......
...@@ -59,9 +59,9 @@ frontend: ...@@ -59,9 +59,9 @@ frontend:
enabled: false enabled: false
environment: environment:
NEXT_PUBLIC_APP_ENV: NEXT_PUBLIC_APP_ENV:
_default: preview _default: development
NEXT_PUBLIC_APP_INSTANCE: NEXT_PUBLIC_APP_INSTANCE:
_default: eth_goerli _default: review
NEXT_PUBLIC_NETWORK_NAME: NEXT_PUBLIC_NETWORK_NAME:
_default: Blockscout _default: Blockscout
NEXT_PUBLIC_NETWORK_ID: NEXT_PUBLIC_NETWORK_ID:
......
...@@ -417,8 +417,8 @@ For the smart contract addresses which are [Safe{Core} accounts](https://safe.gl ...@@ -417,8 +417,8 @@ For the smart contract addresses which are [Safe{Core} accounts](https://safe.gl
| --- | --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_SENTRY_DSN | `string` | Client key for your Sentry.io app | Required | - | `<your-secret>` | | NEXT_PUBLIC_SENTRY_DSN | `string` | Client key for your Sentry.io app | Required | - | `<your-secret>` |
| SENTRY_CSP_REPORT_URI | `string` | URL for sending CSP-reports to your Sentry.io app | - | - | `<your-secret>` | | SENTRY_CSP_REPORT_URI | `string` | URL for sending CSP-reports to your Sentry.io app | - | - | `<your-secret>` |
| NEXT_PUBLIC_APP_ENV | `string` | Current app env (e.g development, review or production). Passed as `environment` property to Sentry config | - | `process.env.NODE_ENV` | `production` | | NEXT_PUBLIC_APP_ENV | `string` | App env (e.g development, review or production). Passed as `environment` property to Sentry config | - | `production` | `production` |
| NEXT_PUBLIC_APP_INSTANCE | `string` | Name of app instance. Used as custom tag `app_instance` value in the main Sentry scope | - | - | `wonderful_kepler` | | NEXT_PUBLIC_APP_INSTANCE | `string` | Name of app instance. Used as custom tag `app_instance` value in the main Sentry scope. If not provided, it will be constructed from `NEXT_PUBLIC_APP_HOST` | - | - | `wonderful_kepler` |
&nbsp; &nbsp;
......
...@@ -52,7 +52,12 @@ export default function useFetch() { ...@@ -52,7 +52,12 @@ export default function useFetch() {
}; };
if (!meta?.omitSentryErrorLog) { if (!meta?.omitSentryErrorLog) {
Sentry.captureException(new Error('Client fetch failed'), { extra: { ...error, ...meta }, tags: { source: 'fetch' } }); Sentry.captureException(new Error('Client fetch failed'), { tags: {
source: 'fetch',
'source.resource': meta?.resource,
'status.code': error.status,
'status.text': error.statusText,
} });
} }
return response.json().then( return response.json().then(
......
...@@ -17,7 +17,12 @@ export default function useGetCsrfToken() { ...@@ -17,7 +17,12 @@ export default function useGetCsrfToken() {
const csrfFromHeader = apiResponse.headers.get('x-bs-account-csrf'); const csrfFromHeader = apiResponse.headers.get('x-bs-account-csrf');
if (!csrfFromHeader) { if (!csrfFromHeader) {
Sentry.captureException(new Error('Unable to get csrf token'), { tags: { source: 'csrf_token' } }); Sentry.captureException(new Error('Client fetch failed'), { tags: {
source: 'fetch',
'source.resource': 'csrf',
'status.code': 500,
'status.text': 'Unable to obtain csrf token from header',
} });
return; return;
} }
......
...@@ -19,7 +19,7 @@ export default function useRedirectForInvalidAuthToken() { ...@@ -19,7 +19,7 @@ export default function useRedirectForInvalidAuthToken() {
const apiToken = cookies.get(cookies.NAMES.API_TOKEN); const apiToken = cookies.get(cookies.NAMES.API_TOKEN);
if (apiToken && loginUrl) { if (apiToken && loginUrl) {
Sentry.captureException(new Error('Invalid api token'), { tags: { source: 'invalid_api_token' } }); Sentry.captureException(new Error('Invalid API token'), { tags: { source: 'invalid_api_token' } });
window.location.assign(loginUrl); window.location.assign(loginUrl);
} }
} }
......
import type * as Sentry from '@sentry/react'; import type * as Sentry from '@sentry/react';
import { BrowserTracing } from '@sentry/tracing';
import appConfig from 'configs/app'; import appConfig from 'configs/app';
...@@ -13,11 +12,8 @@ export const config: Sentry.BrowserOptions | undefined = (() => { ...@@ -13,11 +12,8 @@ export const config: Sentry.BrowserOptions | undefined = (() => {
return { return {
environment: feature.environment, environment: feature.environment,
dsn: feature.dsn, dsn: feature.dsn,
release: process.env.NEXT_PUBLIC_GIT_TAG || process.env.NEXT_PUBLIC_GIT_COMMIT_SHA, release: feature.release,
integrations: [ new BrowserTracing() ], enableTracing: false,
// We recommend adjusting this value in production, or using tracesSampler
// for finer control
tracesSampleRate: 1.0,
// error filtering settings // error filtering settings
// were taken from here - https://docs.sentry.io/platforms/node/guides/azure-functions/configuration/filtering/#decluttering-sentry // were taken from here - https://docs.sentry.io/platforms/node/guides/azure-functions/configuration/filtering/#decluttering-sentry
......
import * as Sentry from '@sentry/react'; import * as Sentry from '@sentry/react';
import React from 'react'; import React from 'react';
import { config, configureScope } from 'configs/sentry/react'; import { config, configureScope } from './config';
export default function useConfigSentry() { export default function useConfigSentry() {
React.useEffect(() => { React.useEffect(() => {
......
...@@ -4,10 +4,10 @@ import React from 'react'; ...@@ -4,10 +4,10 @@ import React from 'react';
import type { Route } from 'nextjs-routes'; import type { Route } from 'nextjs-routes';
import useAdblockDetect from 'lib/hooks/useAdblockDetect'; import useAdblockDetect from 'lib/hooks/useAdblockDetect';
import useConfigSentry from 'lib/hooks/useConfigSentry';
import useGetCsrfToken from 'lib/hooks/useGetCsrfToken'; import useGetCsrfToken from 'lib/hooks/useGetCsrfToken';
import * as metadata from 'lib/metadata'; import * as metadata from 'lib/metadata';
import * as mixpanel from 'lib/mixpanel'; import * as mixpanel from 'lib/mixpanel';
import useConfigSentry from 'lib/sentry/useConfigSentry';
type Props = Route & { type Props = Route & {
children: React.ReactNode; children: React.ReactNode;
......
import * as Sentry from '@sentry/react';
import React from 'react'; import React from 'react';
import type { NextPageWithLayout } from 'nextjs/types'; import type { NextPageWithLayout } from 'nextjs/types';
...@@ -10,6 +11,10 @@ import LayoutError from 'ui/shared/layout/LayoutError'; ...@@ -10,6 +11,10 @@ import LayoutError from 'ui/shared/layout/LayoutError';
const error = new Error('Not found', { cause: { status: 404 } }); const error = new Error('Not found', { cause: { status: 404 } });
const Page: NextPageWithLayout = () => { const Page: NextPageWithLayout = () => {
React.useEffect(() => {
Sentry.captureException(new Error('Page not found'), { tags: { source: '404' } });
}, []);
return ( return (
<PageNextJs pathname="/404"> <PageNextJs pathname="/404">
<AppError error={ error }/> <AppError error={ error }/>
......
/**
* NOTE: This requires `@sentry/nextjs` version 7.3.0 or higher.
*
* NOTE: If using this with `next` version 12.2.0 or lower, uncomment the
* penultimate line in `CustomErrorComponent`.
*
* This page is loaded by Nextjs:
* - on the server, when data-fetching methods throw or reject
* - on the client, when `getInitialProps` throws or rejects
* - on the client, when a React lifecycle method throws or rejects, and it's
* caught by the built-in Nextjs error boundary
*
* See:
* - https://nextjs.org/docs/basic-features/data-fetching/overview
* - https://nextjs.org/docs/api-reference/data-fetching/get-initial-props
* - https://reactjs.org/docs/error-boundaries.html
*/
import * as Sentry from '@sentry/nextjs';
import type { GetServerSideProps } from 'next';
import NextErrorComponent from 'next/error'; import NextErrorComponent from 'next/error';
import React from 'react'; import React from 'react';
import type { Props as ServerSidePropsCommon } from 'nextjs/getServerSideProps'; import type { Props as ServerSidePropsCommon } from 'nextjs/getServerSideProps';
import { base as getServerSidePropsCommon } from 'nextjs/getServerSideProps';
import sentryConfig from 'configs/sentry/nextjs';
import * as cookies from 'lib/cookies'; import * as cookies from 'lib/cookies';
type Props = ServerSidePropsCommon & { type Props = ServerSidePropsCommon & {
...@@ -37,16 +15,3 @@ const CustomErrorComponent = (props: Props) => { ...@@ -37,16 +15,3 @@ const CustomErrorComponent = (props: Props) => {
}; };
export default CustomErrorComponent; export default CustomErrorComponent;
export const getServerSideProps: GetServerSideProps = async(context) => {
Sentry.init(sentryConfig);
// In case this is running in a serverless function, await this in order to give Sentry
// time to send the error before the lambda exits
await Sentry.captureUnderscoreErrorException(context);
const commonSSPResult = await getServerSidePropsCommon(context);
const commonSSProps = 'props' in commonSSPResult ? commonSSPResult.props : undefined;
return { props: { ...commonSSProps, statusCode: context.res.statusCode } };
};
...@@ -26,7 +26,7 @@ export default function useMarketplaceApps(filter: string, selectedCategoryId: s ...@@ -26,7 +26,7 @@ export default function useMarketplaceApps(filter: string, selectedCategoryId: s
const apiFetch = useApiFetch(); const apiFetch = useApiFetch();
const { isPlaceholderData, isError, error, data } = useQuery<unknown, ResourceError<unknown>, Array<MarketplaceAppOverview>>( const { isPlaceholderData, isError, error, data } = useQuery<unknown, ResourceError<unknown>, Array<MarketplaceAppOverview>>(
[ 'marketplace-apps' ], [ 'marketplace-apps' ],
async() => apiFetch(configUrl), async() => apiFetch(configUrl, undefined, { resource: 'marketplace-apps' }),
{ {
select: (data) => (data as Array<MarketplaceAppOverview>).sort((a, b) => a.title.localeCompare(b.title)), select: (data) => (data as Array<MarketplaceAppOverview>).sort((a, b) => a.title.localeCompare(b.title)),
placeholderData: feature.isEnabled ? Array(9).fill(MARKETPLACE_APP) : undefined, placeholderData: feature.isEnabled ? Array(9).fill(MARKETPLACE_APP) : undefined,
......
...@@ -25,7 +25,7 @@ const Login = () => { ...@@ -25,7 +25,7 @@ const Login = () => {
}, []); }, []);
const checkSentry = React.useCallback(() => { const checkSentry = React.useCallback(() => {
Sentry.captureException(new Error('Test error'), { extra: { foo: 'bar' }, tags: { source: 'test' } }); Sentry.captureException(new Error('Test error'), { tags: { source: 'test' } });
}, []); }, []);
const checkMixpanel = React.useCallback(() => { const checkMixpanel = React.useCallback(() => {
......
...@@ -37,7 +37,7 @@ const MarketplaceApp = () => { ...@@ -37,7 +37,7 @@ const MarketplaceApp = () => {
const { isLoading, isError, error, data } = useQuery<unknown, ResourceError<unknown>, MarketplaceAppOverview>( const { isLoading, isError, error, data } = useQuery<unknown, ResourceError<unknown>, MarketplaceAppOverview>(
[ 'marketplace-apps', id ], [ 'marketplace-apps', id ],
async() => { async() => {
const result = await apiFetch<Array<MarketplaceAppOverview>, unknown>(configUrl); const result = await apiFetch<Array<MarketplaceAppOverview>, unknown>(configUrl, undefined, { resource: 'marketplace-apps' });
if (!Array.isArray(result)) { if (!Array.isArray(result)) {
throw result; throw result;
} }
......
...@@ -34,7 +34,7 @@ export default function useNftMediaType(url: string | null, isEnabled: boolean) ...@@ -34,7 +34,7 @@ export default function useNftMediaType(url: string | null, isEnabled: boolean)
try { try {
const mediaTypeResourceUrl = route({ pathname: '/node-api/media-type' as StaticRoute<'/api/media-type'>['pathname'], query: { url } }); const mediaTypeResourceUrl = route({ pathname: '/node-api/media-type' as StaticRoute<'/api/media-type'>['pathname'], query: { url } });
const response = await fetch<{ type: MediaType | undefined }, ResourceError>(mediaTypeResourceUrl); const response = await fetch<{ type: MediaType | undefined }, ResourceError>(mediaTypeResourceUrl, undefined, { resource: 'media-type' });
return 'type' in response ? response.type ?? 'image' : 'image'; return 'type' in response ? response.type ?? 'image' : 'image';
} catch (error) { } catch (error) {
......
...@@ -91,7 +91,7 @@ const Footer = () => { ...@@ -91,7 +91,7 @@ const Footer = () => {
const { isLoading, data: linksData } = useQuery<unknown, ResourceError<unknown>, Array<CustomLinksGroup>>( const { isLoading, data: linksData } = useQuery<unknown, ResourceError<unknown>, Array<CustomLinksGroup>>(
[ 'footer-links' ], [ 'footer-links' ],
async() => fetch(config.UI.footer.links || ''), async() => fetch(config.UI.footer.links || '', undefined, { resource: 'footer-links' }),
{ {
enabled: Boolean(config.UI.footer.links), enabled: Boolean(config.UI.footer.links),
staleTime: Infinity, staleTime: Infinity,
......
...@@ -15,7 +15,7 @@ export default function useNetworkMenu() { ...@@ -15,7 +15,7 @@ export default function useNetworkMenu() {
const apiFetch = useApiFetch(); const apiFetch = useApiFetch();
const { isLoading, data } = useQuery<unknown, ResourceError<unknown>, Array<FeaturedNetwork>>( const { isLoading, data } = useQuery<unknown, ResourceError<unknown>, Array<FeaturedNetwork>>(
[ 'featured-network' ], [ 'featured-network' ],
async() => apiFetch(config.UI.sidebar.featuredNetworks || ''), async() => apiFetch(config.UI.sidebar.featuredNetworks || '', undefined, { resource: 'featured-network' }),
{ {
enabled: Boolean(config.UI.sidebar.featuredNetworks) && isOpen, enabled: Boolean(config.UI.sidebar.featuredNetworks) && isOpen,
staleTime: Infinity, staleTime: Infinity,
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment