Commit 5ba45dbb authored by tom goriunov's avatar tom goriunov Committed by GitHub

Migrate from Sentry.io to Rollbar monitoring solution (#2403)

* basic Rollbar setup

* remove sentry code

* add rollback env to values for demo

* [skip ci] adjust instance name

* fix validator

* don't log errors from API by default and change error levels

* [skip ci] change default value for environment property

* [skip ci] do not log api errors of address endpoint
parent f086f33c
NEXT_PUBLIC_SENTRY_DSN=https://sentry.io NEXT_PUBLIC_ROLLBAR_CLIENT_TOKEN=xxx
SENTRY_CSP_REPORT_URI=https://sentry.io
NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID=xxx NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID=xxx
NEXT_PUBLIC_RE_CAPTCHA_V3_APP_SITE_KEY=xxx NEXT_PUBLIC_RE_CAPTCHA_V3_APP_SITE_KEY=xxx
NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID=UA-XXXXXX-X NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID=UA-XXXXXX-X
......
# TODO @tom2drum setup source maps for Rollbar
name: Upload source maps to Sentry name: Upload source maps to Sentry
on: on:
workflow_call: workflow_call:
......
...@@ -43,9 +43,6 @@ yarn-error.log* ...@@ -43,9 +43,6 @@ yarn-error.log*
.eslintcache .eslintcache
# Sentry
.sentryclirc
**.decrypted~** **.decrypted~**
/test-results/ /test-results/
/playwright-report/ /playwright-report/
......
...@@ -25,10 +25,10 @@ export { default as nameService } from './nameService'; ...@@ -25,10 +25,10 @@ export { default as nameService } from './nameService';
export { default as publicTagsSubmission } from './publicTagsSubmission'; export { default as publicTagsSubmission } from './publicTagsSubmission';
export { default as restApiDocs } from './restApiDocs'; export { default as restApiDocs } from './restApiDocs';
export { default as rewards } from './rewards'; export { default as rewards } from './rewards';
export { default as rollbar } from './rollbar';
export { default as rollup } from './rollup'; export { default as rollup } from './rollup';
export { default as safe } from './safe'; export { default as safe } from './safe';
export { default as saveOnGas } from './saveOnGas'; export { default as saveOnGas } from './saveOnGas';
export { default as sentry } from './sentry';
export { default as sol2uml } from './sol2uml'; export { default as sol2uml } from './sol2uml';
export { default as stats } from './stats'; export { default as stats } from './stats';
export { default as suave } from './suave'; export { default as suave } from './suave';
......
...@@ -3,35 +3,34 @@ import type { Feature } from './types'; ...@@ -3,35 +3,34 @@ import type { Feature } from './types';
import app from '../app'; import app from '../app';
import { getEnvValue } from '../utils'; import { getEnvValue } from '../utils';
const dsn = getEnvValue('NEXT_PUBLIC_SENTRY_DSN'); const clientToken = getEnvValue('NEXT_PUBLIC_ROLLBAR_CLIENT_TOKEN');
const instance = (() => { const instance = (() => {
const envValue = getEnvValue('NEXT_PUBLIC_APP_INSTANCE'); const envValue = getEnvValue('NEXT_PUBLIC_APP_INSTANCE');
if (envValue) { if (envValue) {
return envValue; return envValue;
} }
return app.host?.replace('.blockscout.com', '').replaceAll('-', '_'); return app.host?.replace('.blockscout.com', '').replace('.k8s-dev', '').replaceAll('-', '_');
})(); })();
const environment = getEnvValue('NEXT_PUBLIC_APP_ENV') || 'production'; const environment = getEnvValue('NEXT_PUBLIC_APP_ENV') || 'production';
const release = getEnvValue('NEXT_PUBLIC_GIT_TAG'); const codeVersion = getEnvValue('NEXT_PUBLIC_GIT_TAG') || getEnvValue('NEXT_PUBLIC_GIT_COMMIT_SHA');
const title = 'Sentry error monitoring';
const title = 'Rollbar error monitoring';
const config: Feature<{ const config: Feature<{
dsn: string; clientToken: string;
instance: string;
release: string | undefined;
environment: string; environment: string;
enableTracing: boolean; instance: string | undefined;
codeVersion: string | undefined;
}> = (() => { }> = (() => {
if (dsn && instance && environment) { if (clientToken) {
return Object.freeze({ return Object.freeze({
title, title,
isEnabled: true, isEnabled: true,
dsn, clientToken,
instance,
release,
environment, environment,
enableTracing: getEnvValue('NEXT_PUBLIC_SENTRY_ENABLE_TRACING') === 'true', instance,
codeVersion,
}); });
} }
......
...@@ -61,7 +61,6 @@ NEXT_PUBLIC_OTHER_LINKS=[{'url':'https://eth.drpc.org?ref=559183','text':'Public ...@@ -61,7 +61,6 @@ NEXT_PUBLIC_OTHER_LINKS=[{'url':'https://eth.drpc.org?ref=559183','text':'Public
NEXT_PUBLIC_REWARDS_SERVICE_API_HOST=https://merits.blockscout.com NEXT_PUBLIC_REWARDS_SERVICE_API_HOST=https://merits.blockscout.com
NEXT_PUBLIC_SAFE_TX_SERVICE_URL=https://safe-transaction-mainnet.safe.global NEXT_PUBLIC_SAFE_TX_SERVICE_URL=https://safe-transaction-mainnet.safe.global
NEXT_PUBLIC_SAVE_ON_GAS_ENABLED=true NEXT_PUBLIC_SAVE_ON_GAS_ENABLED=true
NEXT_PUBLIC_SENTRY_ENABLE_TRACING=true
NEXT_PUBLIC_SEO_ENHANCED_DATA_ENABLED=true NEXT_PUBLIC_SEO_ENHANCED_DATA_ENABLED=true
NEXT_PUBLIC_STATS_API_HOST=https://stats-eth-main.k8s-prod-1.blockscout.com NEXT_PUBLIC_STATS_API_HOST=https://stats-eth-main.k8s-prod-1.blockscout.com
NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=blockscout NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=blockscout
......
...@@ -64,7 +64,6 @@ NEXT_PUBLIC_OG_IMAGE_URL=https://raw.githubusercontent.com/blockscout/frontend-c ...@@ -64,7 +64,6 @@ NEXT_PUBLIC_OG_IMAGE_URL=https://raw.githubusercontent.com/blockscout/frontend-c
NEXT_PUBLIC_OTHER_LINKS=[{'url':'https://sepolia.drpc.org?ref=559183','text':'Public RPC'}] NEXT_PUBLIC_OTHER_LINKS=[{'url':'https://sepolia.drpc.org?ref=559183','text':'Public RPC'}]
NEXT_PUBLIC_REWARDS_SERVICE_API_HOST=https://merits.blockscout.com NEXT_PUBLIC_REWARDS_SERVICE_API_HOST=https://merits.blockscout.com
NEXT_PUBLIC_SAFE_TX_SERVICE_URL=https://safe-transaction-sepolia.safe.global NEXT_PUBLIC_SAFE_TX_SERVICE_URL=https://safe-transaction-sepolia.safe.global
NEXT_PUBLIC_SENTRY_ENABLE_TRACING=true
NEXT_PUBLIC_STATS_API_HOST=https://stats-sepolia.k8s.blockscout.com NEXT_PUBLIC_STATS_API_HOST=https://stats-sepolia.k8s.blockscout.com
NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=noves NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=noves
NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED=true NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED=true
......
...@@ -7,6 +7,7 @@ NEXT_PUBLIC_APP_PROTOCOL=http ...@@ -7,6 +7,7 @@ NEXT_PUBLIC_APP_PROTOCOL=http
NEXT_PUBLIC_APP_HOST=localhost NEXT_PUBLIC_APP_HOST=localhost
NEXT_PUBLIC_APP_PORT=3000 NEXT_PUBLIC_APP_PORT=3000
NEXT_PUBLIC_APP_ENV=development NEXT_PUBLIC_APP_ENV=development
NEXT_PUBLIC_APP_INSTANCE=rubber_duck
NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL=ws NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL=ws
# Instance ENVs # Instance ENVs
...@@ -60,7 +61,6 @@ NEXT_PUBLIC_OG_ENHANCED_DATA_ENABLED=true ...@@ -60,7 +61,6 @@ NEXT_PUBLIC_OG_ENHANCED_DATA_ENABLED=true
NEXT_PUBLIC_OG_IMAGE_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/og-images/sepolia-testnet.png NEXT_PUBLIC_OG_IMAGE_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/og-images/sepolia-testnet.png
NEXT_PUBLIC_OTHER_LINKS=[{'url':'https://sepolia.drpc.org?ref=559183','text':'Public RPC'}] NEXT_PUBLIC_OTHER_LINKS=[{'url':'https://sepolia.drpc.org?ref=559183','text':'Public RPC'}]
NEXT_PUBLIC_SAFE_TX_SERVICE_URL=https://safe-transaction-sepolia.safe.global NEXT_PUBLIC_SAFE_TX_SERVICE_URL=https://safe-transaction-sepolia.safe.global
NEXT_PUBLIC_SENTRY_ENABLE_TRACING=true
NEXT_PUBLIC_SEO_ENHANCED_DATA_ENABLED=true NEXT_PUBLIC_SEO_ENHANCED_DATA_ENABLED=true
NEXT_PUBLIC_STATS_API_HOST=https://stats-sepolia.k8s-dev.blockscout.com NEXT_PUBLIC_STATS_API_HOST=https://stats-sepolia.k8s-dev.blockscout.com
NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=blockscout NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=blockscout
......
...@@ -147,6 +147,16 @@ function printDeprecationWarning(envsMap: Record<string, string>) { ...@@ -147,6 +147,16 @@ function printDeprecationWarning(envsMap: Record<string, string>) {
console.log('❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗\n'); console.log('❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗\n');
} }
if (
(envsMap.NEXT_PUBLIC_SENTRY_DSN || envsMap.SENTRY_CSP_REPORT_URI || envsMap.NEXT_PUBLIC_SENTRY_ENABLE_TRACING) &&
envsMap.NEXT_PUBLIC_ROLLBAR_CLIENT_TOKEN
) {
console.log('❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗');
// eslint-disable-next-line max-len
console.warn('The Sentry monitoring is now deprecated and will be removed in the next release. Please migrate to the Rollbar error monitoring.');
console.log('❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗\n');
}
if ( if (
envsMap.NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR || envsMap.NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR ||
envsMap.NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND envsMap.NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND
...@@ -178,5 +188,14 @@ function checkDeprecatedEnvs(envsMap: Record<string, string>) { ...@@ -178,5 +188,14 @@ function checkDeprecatedEnvs(envsMap: Record<string, string>) {
throw new Error(); throw new Error();
} }
if (
(envsMap.NEXT_PUBLIC_SENTRY_DSN || envsMap.SENTRY_CSP_REPORT_URI || envsMap.NEXT_PUBLIC_SENTRY_ENABLE_TRACING) &&
!envsMap.NEXT_PUBLIC_ROLLBAR_CLIENT_TOKEN
) {
// eslint-disable-next-line max-len
console.log('🚨 The Sentry error monitoring is no longer supported. Please migrate to the Rollbar error monitoring.');
throw new Error();
}
!silent && console.log('👍 All good!\n'); !silent && console.log('👍 All good!\n');
} }
...@@ -372,6 +372,7 @@ const adsBannerSchema = yup ...@@ -372,6 +372,7 @@ const adsBannerSchema = yup
NEXT_PUBLIC_AD_ADBUTLER_CONFIG_MOBILE: adButlerConfigSchema, NEXT_PUBLIC_AD_ADBUTLER_CONFIG_MOBILE: adButlerConfigSchema,
}); });
// DEPRECATED
const sentrySchema = yup const sentrySchema = yup
.object() .object()
.shape({ .shape({
...@@ -389,20 +390,6 @@ const sentrySchema = yup ...@@ -389,20 +390,6 @@ const sentrySchema = yup
is: (value: string) => Boolean(value), is: (value: string) => Boolean(value),
then: (schema) => schema, then: (schema) => schema,
}), }),
NEXT_PUBLIC_APP_INSTANCE: yup
.string()
.when('NEXT_PUBLIC_SENTRY_DSN', {
is: (value: string) => Boolean(value),
then: (schema) => schema,
otherwise: (schema) => schema.max(-1, 'NEXT_PUBLIC_APP_INSTANCE cannot not be used without NEXT_PUBLIC_SENTRY_DSN'),
}),
NEXT_PUBLIC_APP_ENV: yup
.string()
.when('NEXT_PUBLIC_SENTRY_DSN', {
is: (value: string) => Boolean(value),
then: (schema) => schema,
otherwise: (schema) => schema.max(-1, 'NEXT_PUBLIC_APP_ENV cannot not be used without NEXT_PUBLIC_SENTRY_DSN'),
}),
}); });
const accountSchema = yup const accountSchema = yup
...@@ -592,6 +579,8 @@ const schema = yup ...@@ -592,6 +579,8 @@ const schema = yup
NEXT_PUBLIC_APP_HOST: yup.string().required(), NEXT_PUBLIC_APP_HOST: yup.string().required(),
NEXT_PUBLIC_APP_PROTOCOL: yup.string().oneOf(protocols), NEXT_PUBLIC_APP_PROTOCOL: yup.string().oneOf(protocols),
NEXT_PUBLIC_APP_PORT: yup.number().positive().integer(), NEXT_PUBLIC_APP_PORT: yup.number().positive().integer(),
NEXT_PUBLIC_APP_ENV: yup.string(),
NEXT_PUBLIC_APP_INSTANCE: yup.string(),
// 2. Blockchain parameters // 2. Blockchain parameters
NEXT_PUBLIC_NETWORK_NAME: yup.string().required(), NEXT_PUBLIC_NETWORK_NAME: yup.string().required(),
...@@ -898,6 +887,7 @@ const schema = yup ...@@ -898,6 +887,7 @@ const schema = yup
NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID: yup.string(), NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID: yup.string(),
NEXT_PUBLIC_MIXPANEL_PROJECT_TOKEN: yup.string(), NEXT_PUBLIC_MIXPANEL_PROJECT_TOKEN: yup.string(),
NEXT_PUBLIC_GROWTH_BOOK_CLIENT_KEY: yup.string(), NEXT_PUBLIC_GROWTH_BOOK_CLIENT_KEY: yup.string(),
NEXT_PUBLIC_ROLLBAR_CLIENT_TOKEN: yup.string(),
// Misc // Misc
NEXT_PUBLIC_USE_NEXT_JS_PROXY: yup.boolean(), NEXT_PUBLIC_USE_NEXT_JS_PROXY: yup.boolean(),
......
NEXT_PUBLIC_SENTRY_DSN=https://sentry.io NEXT_PUBLIC_ROLLBAR_CLIENT_TOKEN=https://rollbar.com
NEXT_PUBLIC_AUTH_URL=https://example.com NEXT_PUBLIC_AUTH_URL=https://example.com
NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true
NEXT_PUBLIC_LOGOUT_URL=https://example.com NEXT_PUBLIC_LOGOUT_URL=https://example.com
......
NEXT_PUBLIC_SENTRY_DSN=https://sentry.io
SENTRY_CSP_REPORT_URI=https://sentry.io
NEXT_PUBLIC_SENTRY_ENABLE_TRACING=true
NEXT_PUBLIC_APP_ENV=production
NEXT_PUBLIC_APP_INSTANCE=duck
\ No newline at end of file
...@@ -47,8 +47,7 @@ frontend: ...@@ -47,8 +47,7 @@ frontend:
memory: 384Mi memory: 384Mi
cpu: 250m cpu: 250m
env: env:
NEXT_PUBLIC_APP_ENV: development NEXT_PUBLIC_APP_ENV: review
NEXT_PUBLIC_APP_INSTANCE: review_L2
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
NEXT_PUBLIC_FEATURED_NETWORKS: https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/featured-networks/base-mainnet.json NEXT_PUBLIC_FEATURED_NETWORKS: https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/featured-networks/base-mainnet.json
...@@ -73,8 +72,6 @@ frontend: ...@@ -73,8 +72,6 @@ frontend:
NEXT_PUBLIC_NAVIGATION_LAYOUT: horizontal NEXT_PUBLIC_NAVIGATION_LAYOUT: horizontal
NEXT_PUBLIC_NAVIGATION_HIGHLIGHTED_ROUTES: "['/blocks','/name-domains']" NEXT_PUBLIC_NAVIGATION_HIGHLIGHTED_ROUTES: "['/blocks','/name-domains']"
envFromSecret: envFromSecret:
NEXT_PUBLIC_SENTRY_DSN: ref+vault://deployment-values/blockscout/dev/review-l2?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_SENTRY_DSN
SENTRY_CSP_REPORT_URI: ref+vault://deployment-values/blockscout/dev/review-l2?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/SENTRY_CSP_REPORT_URI
NEXT_PUBLIC_AUTH0_CLIENT_ID: ref+vault://deployment-values/blockscout/dev/review-l2?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_AUTH0_CLIENT_ID NEXT_PUBLIC_AUTH0_CLIENT_ID: ref+vault://deployment-values/blockscout/dev/review-l2?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_AUTH0_CLIENT_ID
NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID: ref+vault://deployment-values/blockscout/dev/review-l2?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID: ref+vault://deployment-values/blockscout/dev/review-l2?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID
NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID: ref+vault://deployment-values/blockscout/dev/review-l2?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID: ref+vault://deployment-values/blockscout/dev/review-l2?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID
......
...@@ -47,8 +47,7 @@ frontend: ...@@ -47,8 +47,7 @@ frontend:
memory: 384Mi memory: 384Mi
cpu: 250m cpu: 250m
env: env:
NEXT_PUBLIC_APP_ENV: development NEXT_PUBLIC_APP_ENV: review
NEXT_PUBLIC_APP_INSTANCE: review
NEXT_PUBLIC_FEATURED_NETWORKS: https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/featured-networks/eth-sepolia.json NEXT_PUBLIC_FEATURED_NETWORKS: https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/featured-networks/eth-sepolia.json
NEXT_PUBLIC_NETWORK_LOGO: https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/sepolia.svg NEXT_PUBLIC_NETWORK_LOGO: https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/sepolia.svg
NEXT_PUBLIC_NETWORK_ICON: https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/sepolia.png NEXT_PUBLIC_NETWORK_ICON: https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/sepolia.png
...@@ -79,11 +78,10 @@ frontend: ...@@ -79,11 +78,10 @@ frontend:
NEXT_PUBLIC_NAVIGATION_HIGHLIGHTED_ROUTES: "['/apps']" NEXT_PUBLIC_NAVIGATION_HIGHLIGHTED_ROUTES: "['/apps']"
PROMETHEUS_METRICS_ENABLED: true PROMETHEUS_METRICS_ENABLED: true
envFromSecret: envFromSecret:
NEXT_PUBLIC_SENTRY_DSN: ref+vault://deployment-values/blockscout/dev/review?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_SENTRY_DSN
SENTRY_CSP_REPORT_URI: ref+vault://deployment-values/blockscout/dev/review?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/SENTRY_CSP_REPORT_URI
NEXT_PUBLIC_AUTH0_CLIENT_ID: ref+vault://deployment-values/blockscout/dev/review?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_AUTH0_CLIENT_ID NEXT_PUBLIC_AUTH0_CLIENT_ID: ref+vault://deployment-values/blockscout/dev/review?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_AUTH0_CLIENT_ID
NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID: ref+vault://deployment-values/blockscout/dev/review?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID: ref+vault://deployment-values/blockscout/dev/review?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID
NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID: ref+vault://deployment-values/blockscout/dev/review?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID: ref+vault://deployment-values/blockscout/dev/review?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID
NEXT_PUBLIC_GROWTH_BOOK_CLIENT_KEY: ref+vault://deployment-values/blockscout/dev/review?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_GROWTH_BOOK_CLIENT_KEY NEXT_PUBLIC_GROWTH_BOOK_CLIENT_KEY: ref+vault://deployment-values/blockscout/dev/review?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_GROWTH_BOOK_CLIENT_KEY
NEXT_PUBLIC_MIXPANEL_PROJECT_TOKEN: ref+vault://deployment-values/blockscout/common?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_MIXPANEL_PROJECT_TOKEN NEXT_PUBLIC_MIXPANEL_PROJECT_TOKEN: ref+vault://deployment-values/blockscout/common?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_MIXPANEL_PROJECT_TOKEN
NEXT_PUBLIC_RE_CAPTCHA_V3_APP_SITE_KEY: ref+vault://deployment-values/blockscout/common?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_RE_CAPTCHA_V3_APP_SITE_KEY NEXT_PUBLIC_RE_CAPTCHA_V3_APP_SITE_KEY: ref+vault://deployment-values/blockscout/common?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_RE_CAPTCHA_V3_APP_SITE_KEY
NEXT_PUBLIC_ROLLBAR_CLIENT_TOKEN: ref+vault://deployment-values/blockscout/common?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_ROLLBAR_CLIENT_TOKEN
...@@ -60,6 +60,7 @@ Please be aware that all environment variables prefixed with `NEXT_PUBLIC_` will ...@@ -60,6 +60,7 @@ Please be aware that all environment variables prefixed with `NEXT_PUBLIC_` will
- [MetaSuites extension](ENVS.md#metasuites-extension) - [MetaSuites extension](ENVS.md#metasuites-extension)
- [Validators list](ENVS.md#validators-list) - [Validators list](ENVS.md#validators-list)
- [Sentry error monitoring](ENVS.md#sentry-error-monitoring) - [Sentry error monitoring](ENVS.md#sentry-error-monitoring)
- [Rollbar error monitoring](ENVS.md#rollbar-error-monitoring)
- [OpenTelemetry](ENVS.md#opentelemetry) - [OpenTelemetry](ENVS.md#opentelemetry)
- [DeFi dropdown](ENVS.md#defi-dropdown) - [DeFi dropdown](ENVS.md#defi-dropdown)
- [Multichain balance button](ENVS.md#multichain-balance-button) - [Multichain balance button](ENVS.md#multichain-balance-button)
...@@ -77,6 +78,8 @@ Please be aware that all environment variables prefixed with `NEXT_PUBLIC_` will ...@@ -77,6 +78,8 @@ Please be aware that all environment variables prefixed with `NEXT_PUBLIC_` will
| NEXT_PUBLIC_APP_PROTOCOL | `http \| https` | App url schema | - | `https` | `http` | v1.0.x+ | | NEXT_PUBLIC_APP_PROTOCOL | `http \| https` | App url schema | - | `https` | `http` | v1.0.x+ |
| NEXT_PUBLIC_APP_HOST | `string` | App host | Required | - | `blockscout.com` | v1.0.x+ | | NEXT_PUBLIC_APP_HOST | `string` | App host | Required | - | `blockscout.com` | v1.0.x+ |
| NEXT_PUBLIC_APP_PORT | `number` | Port where app is running | - | `3000` | `3001` | v1.0.x+ | | NEXT_PUBLIC_APP_PORT | `number` | Port where app is running | - | `3000` | `3001` | v1.0.x+ |
| NEXT_PUBLIC_APP_ENV | `string` | App env (e.g development, staging, production, etc.). | - | `production` | `staging` | v1.0.x+ |
| NEXT_PUBLIC_APP_INSTANCE | `string` | Name of app instance. Used for app monitoring purposes. If not provided, it will be constructed from `NEXT_PUBLIC_APP_HOST` | - | - | `wonderful_kepler` | v1.0.x+ |
| NEXT_PUBLIC_USE_NEXT_JS_PROXY | `boolean` | Tells the app to proxy all APIs request through the NextJS app. **We strongly advise not to use it in the production environment**, since it can lead to performance issues of the NodeJS server | - | `false` | `true` | v1.8.0+ | | NEXT_PUBLIC_USE_NEXT_JS_PROXY | `boolean` | Tells the app to proxy all APIs request through the NextJS app. **We strongly advise not to use it in the production environment**, since it can lead to performance issues of the NodeJS server | - | `false` | `true` | v1.8.0+ |
&nbsp; &nbsp;
...@@ -738,13 +741,21 @@ The feature enables the Validators page which provides detailed information abou ...@@ -738,13 +741,21 @@ The feature enables the Validators page which provides detailed information abou
### Sentry error monitoring ### Sentry error monitoring
_Note_ This feature is **deprecated**. All ENV variables will be removed in the future releases.
| Variable | Type| Description | Compulsoriness | Default value | Example value | Version | | Variable | Type| Description | Compulsoriness | Default value | Example value | Version |
| --- | --- | --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_SENTRY_DSN | `string` | Client key for your Sentry.io app | Required | - | `<your-secret>` | v1.0.x+ | | NEXT_PUBLIC_SENTRY_DSN | `string` | Client key for your Sentry.io app | Required | - | `<your-secret>` | v1.0.x+ |
| SENTRY_CSP_REPORT_URI | `string` | URL for sending CSP-reports to your Sentry.io app | - | - | `<your-secret>` | v1.0.x+ | | SENTRY_CSP_REPORT_URI | `string` | URL for sending CSP-reports to your Sentry.io app | - | - | `<your-secret>` | v1.0.x+ |
| NEXT_PUBLIC_SENTRY_ENABLE_TRACING | `boolean` | Enables tracing and performance monitoring in Sentry.io | - | `false` | `true` | v1.17.0+ | | NEXT_PUBLIC_SENTRY_ENABLE_TRACING | `boolean` | Enables tracing and performance monitoring in Sentry.io | - | `false` | `true` | v1.17.0+ |
| NEXT_PUBLIC_APP_ENV | `string` | App env (e.g development, review or production). Passed as `environment` property to Sentry config | - | `production` | `production` | v1.0.x+ |
| 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` | v1.0.x+ | &nbsp;
### Rollbar error monitoring
| Variable | Type| Description | Compulsoriness | Default value | Example value | Version |
| --- | --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_ROLLBAR_CLIENT_TOKEN | `string` | Client token for your Rollbar project | Required | - | `<your-secret>` | v1.37.x+ |
&nbsp; &nbsp;
......
...@@ -21,6 +21,7 @@ export interface Params<R extends ResourceName> { ...@@ -21,6 +21,7 @@ export interface Params<R extends ResourceName> {
pathParams?: ResourcePathParams<R>; pathParams?: ResourcePathParams<R>;
queryParams?: Record<string, string | Array<string> | number | boolean | undefined | null>; queryParams?: Record<string, string | Array<string> | number | boolean | undefined | null>;
fetchParams?: Pick<FetchParams, 'body' | 'method' | 'signal' | 'headers'>; fetchParams?: Pick<FetchParams, 'body' | 'method' | 'signal' | 'headers'>;
logError?: boolean;
} }
export default function useApiFetch() { export default function useApiFetch() {
...@@ -30,7 +31,7 @@ export default function useApiFetch() { ...@@ -30,7 +31,7 @@ export default function useApiFetch() {
return React.useCallback(<R extends ResourceName, SuccessType = unknown, ErrorType = unknown>( return React.useCallback(<R extends ResourceName, SuccessType = unknown, ErrorType = unknown>(
resourceName: R, resourceName: R,
{ pathParams, queryParams, fetchParams }: Params<R> = {}, { pathParams, queryParams, fetchParams, logError }: Params<R> = {},
) => { ) => {
const apiToken = cookies.get(cookies.NAMES.API_TOKEN); const apiToken = cookies.get(cookies.NAMES.API_TOKEN);
...@@ -58,7 +59,7 @@ export default function useApiFetch() { ...@@ -58,7 +59,7 @@ export default function useApiFetch() {
}, },
{ {
resource: resource.path, resource: resource.path,
omitSentryErrorLog: true, // disable logging of API errors to Sentry logError,
}, },
); );
}, [ fetch, csrfToken ]); }, [ fetch, csrfToken ]);
......
...@@ -11,6 +11,7 @@ export interface Params<R extends ResourceName, E = unknown, D = ResourcePayload ...@@ -11,6 +11,7 @@ export interface Params<R extends ResourceName, E = unknown, D = ResourcePayload
queryParams?: Record<string, string | Array<string> | number | boolean | undefined>; queryParams?: Record<string, string | Array<string> | number | boolean | undefined>;
fetchParams?: Pick<FetchParams, 'body' | 'method' | 'headers'>; fetchParams?: Pick<FetchParams, 'body' | 'method' | 'headers'>;
queryOptions?: Partial<Omit<UseQueryOptions<ResourcePayload<R>, ResourceError<E>, D>, 'queryFn'>>; queryOptions?: Partial<Omit<UseQueryOptions<ResourcePayload<R>, ResourceError<E>, D>, 'queryFn'>>;
logError?: boolean;
} }
export function getResourceKey<R extends ResourceName>(resource: R, { pathParams, queryParams }: Params<R> = {}) { export function getResourceKey<R extends ResourceName>(resource: R, { pathParams, queryParams }: Params<R> = {}) {
...@@ -23,7 +24,7 @@ export function getResourceKey<R extends ResourceName>(resource: R, { pathParams ...@@ -23,7 +24,7 @@ export function getResourceKey<R extends ResourceName>(resource: R, { pathParams
export default function useApiQuery<R extends ResourceName, E = unknown, D = ResourcePayload<R>>( export default function useApiQuery<R extends ResourceName, E = unknown, D = ResourcePayload<R>>(
resource: R, resource: R,
{ queryOptions, pathParams, queryParams, fetchParams }: Params<R, E, D> = {}, { queryOptions, pathParams, queryParams, fetchParams, logError }: Params<R, E, D> = {},
) { ) {
const apiFetch = useApiFetch(); const apiFetch = useApiFetch();
...@@ -33,7 +34,7 @@ export default function useApiQuery<R extends ResourceName, E = unknown, D = Res ...@@ -33,7 +34,7 @@ export default function useApiQuery<R extends ResourceName, E = unknown, D = Res
// all errors and error typing is handled by react-query // all errors and error typing is handled by react-query
// so error response will never go to the data // so error response will never go to the data
// that's why we are safe here to do type conversion "as Promise<ResourcePayload<R>>" // that's why we are safe here to do type conversion "as Promise<ResourcePayload<R>>"
return apiFetch(resource, { pathParams, queryParams, fetchParams: { ...fetchParams, signal } }) as Promise<ResourcePayload<R>>; return apiFetch(resource, { pathParams, queryParams, logError, fetchParams: { ...fetchParams, signal } }) as Promise<ResourcePayload<R>>;
}, },
...queryOptions, ...queryOptions,
}); });
......
...@@ -27,7 +27,7 @@ export default function useAddressProfileApiQuery(hash: string | undefined, isEn ...@@ -27,7 +27,7 @@ export default function useAddressProfileApiQuery(hash: string | undefined, isEn
return Promise.reject(); return Promise.reject();
} }
return fetch(feature.apiUrlTemplate.replace('{address}', hash), undefined, { omitSentryErrorLog: true }); return fetch(feature.apiUrlTemplate.replace('{address}', hash));
}, },
enabled: isEnabled && Boolean(hash), enabled: isEnabled && Boolean(hash),
refetchOnMount: false, refetchOnMount: false,
......
import * as Sentry from '@sentry/react';
import React from 'react'; import React from 'react';
import isBodyAllowed from 'lib/api/isBodyAllowed'; import isBodyAllowed from 'lib/api/isBodyAllowed';
import type { ResourceError, ResourcePath } from 'lib/api/resources'; import type { ResourceError, ResourcePath } from 'lib/api/resources';
import { useRollbar } from 'lib/rollbar';
export interface Params { export interface Params {
method?: RequestInit['method']; method?: RequestInit['method'];
...@@ -14,10 +14,12 @@ export interface Params { ...@@ -14,10 +14,12 @@ export interface Params {
interface Meta { interface Meta {
resource?: ResourcePath; resource?: ResourcePath;
omitSentryErrorLog?: boolean; logError?: boolean;
} }
export default function useFetch() { export default function useFetch() {
const rollbar = useRollbar();
return React.useCallback(<Success, Error>(path: string, params?: Params, meta?: Meta): Promise<Success | ResourceError<Error>> => { return React.useCallback(<Success, Error>(path: string, params?: Params, meta?: Meta): Promise<Success | ResourceError<Error>> => {
const _body = params?.body; const _body = params?.body;
const isFormData = _body instanceof FormData; const isFormData = _body instanceof FormData;
...@@ -51,13 +53,12 @@ export default function useFetch() { ...@@ -51,13 +53,12 @@ export default function useFetch() {
statusText: response.statusText, statusText: response.statusText,
}; };
if (!meta?.omitSentryErrorLog) { if (meta?.logError && rollbar) {
Sentry.captureException(new Error('Client fetch failed'), { tags: { rollbar.warn('Client fetch failed', {
source: 'fetch', resource: meta?.resource,
'source.resource': meta?.resource, status_code: error.status,
'status.code': error.status, status_text: error.statusText,
'status.text': error.statusText, });
} });
} }
return response.json().then( return response.json().then(
...@@ -75,5 +76,5 @@ export default function useFetch() { ...@@ -75,5 +76,5 @@ export default function useFetch() {
return response.json() as Promise<Success>; return response.json() as Promise<Success>;
} }
}); });
}, [ ]); }, [ rollbar ]);
} }
import * as Sentry from '@sentry/react';
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import buildUrl from 'lib/api/buildUrl'; import buildUrl from 'lib/api/buildUrl';
...@@ -6,9 +5,11 @@ import isNeedProxy from 'lib/api/isNeedProxy'; ...@@ -6,9 +5,11 @@ import isNeedProxy from 'lib/api/isNeedProxy';
import { getResourceKey } from 'lib/api/useApiQuery'; import { getResourceKey } from 'lib/api/useApiQuery';
import * as cookies from 'lib/cookies'; import * as cookies from 'lib/cookies';
import useFetch from 'lib/hooks/useFetch'; import useFetch from 'lib/hooks/useFetch';
import { useRollbar } from 'lib/rollbar';
export default function useGetCsrfToken() { export default function useGetCsrfToken() {
const nodeApiFetch = useFetch(); const nodeApiFetch = useFetch();
const rollbar = useRollbar();
return useQuery({ return useQuery({
queryKey: getResourceKey('csrf'), queryKey: getResourceKey('csrf'),
...@@ -19,12 +20,11 @@ export default function useGetCsrfToken() { ...@@ -19,12 +20,11 @@ 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('Client fetch failed'), { tags: { rollbar?.warn('Client fetch failed', {
source: 'fetch', resource: 'csrf',
'source.resource': 'csrf', status_code: 500,
'status.code': 500, status_text: 'Unable to obtain csrf token from header',
'status.text': 'Unable to obtain csrf token from header', });
} });
return; return;
} }
......
...@@ -15,7 +15,7 @@ export default function useIsSafeAddress(hash: string | undefined): boolean { ...@@ -15,7 +15,7 @@ export default function useIsSafeAddress(hash: string | undefined): boolean {
return Promise.reject(); return Promise.reject();
} }
return fetch(`${ feature.apiUrl }/${ hash }`, undefined, { omitSentryErrorLog: true }); return fetch(`${ feature.apiUrl }/${ hash }`);
}, },
enabled: feature.isEnabled && Boolean(hash), enabled: feature.isEnabled && Boolean(hash),
refetchOnMount: false, refetchOnMount: false,
......
import { Provider as DefaultProvider, useRollbar as useRollbarDefault } from '@rollbar/react';
import type React from 'react';
import type { Configuration } from 'rollbar';
import config from 'configs/app';
const feature = config.features.rollbar;
const FallbackProvider = ({ children }: { children: React.ReactNode }) => children;
const useRollbarFallback = (): undefined => {};
export const Provider = feature.isEnabled ? DefaultProvider : FallbackProvider;
export const useRollbar = feature.isEnabled ? useRollbarDefault : useRollbarFallback;
export const clientConfig: Configuration | undefined = feature.isEnabled ? {
accessToken: feature.clientToken,
environment: feature.environment,
payload: {
code_version: feature.codeVersion,
app_instance: feature.instance,
},
} : undefined;
import * as Sentry from '@sentry/react';
import { BrowserTracing } from '@sentry/tracing';
import appConfig from 'configs/app';
import { RESOURCE_LOAD_ERROR_MESSAGE } from 'lib/errors/throwOnResourceLoadError';
const feature = appConfig.features.sentry;
export const config: Sentry.BrowserOptions | undefined = (() => {
if (!feature.isEnabled) {
return;
}
const tracesSampleRate: number | undefined = (() => {
switch (feature.environment) {
case 'development':
return 1;
case 'staging':
return 0.75;
case 'production':
return 0.2;
}
})();
return {
environment: feature.environment,
dsn: feature.dsn,
release: feature.release,
enableTracing: feature.enableTracing,
tracesSampleRate,
integrations: feature.enableTracing ? [ new BrowserTracing() ] : undefined,
// 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.',
// Relay and WalletConnect errors
'The quota has been exceeded',
'Attempt to connect to relay via',
'WebSocket connection failed for URL: wss://relay.walletconnect.com',
// API errors
RESOURCE_LOAD_ERROR_MESSAGE,
],
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 and other extensions
/extensions\//i,
/^chrome:\/\//i,
/^chrome-extension:\/\//i,
/^moz-extension:\/\//i,
// Other plugins
/127\.0\.0\.1:4001\/isrunning/i, // Cacaoweb
/webappstoolbarba\.texthelp\.com\//i,
/metrics\.itunes\.apple\.com\.edgesuite\.net\//i,
// AD fetch failed errors
/czilladx\.com/i,
/coinzilla\.com/i,
/coinzilla\.io/i,
/slise\.xyz/i,
],
};
})();
export function configureScope(scope: Sentry.Scope) {
if (!feature.isEnabled) {
return;
}
scope.setTag('app_instance', feature.instance);
}
export function init() {
if (!config) {
return;
}
Sentry.init(config);
Sentry.configureScope(configureScope);
}
...@@ -18,13 +18,7 @@ const moduleExports = { ...@@ -18,13 +18,7 @@ const moduleExports = {
'swagger-ui-react', 'swagger-ui-react',
], ],
reactStrictMode: true, reactStrictMode: true,
webpack(config, { webpack }) { webpack(config) {
config.plugins.push(
new webpack.DefinePlugin({
__SENTRY_DEBUG__: false,
__SENTRY_TRACING__: false,
}),
);
config.module.rules.push( config.module.rules.push(
{ {
test: /\.svg$/, test: /\.svg$/,
......
...@@ -9,7 +9,6 @@ import useAdblockDetect from 'lib/hooks/useAdblockDetect'; ...@@ -9,7 +9,6 @@ import useAdblockDetect from 'lib/hooks/useAdblockDetect';
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 { init as initSentry } from 'lib/sentry/config';
interface Props<Pathname extends Route['pathname']> { interface Props<Pathname extends Route['pathname']> {
pathname: Pathname; pathname: Pathname;
...@@ -18,8 +17,6 @@ interface Props<Pathname extends Route['pathname']> { ...@@ -18,8 +17,6 @@ interface Props<Pathname extends Route['pathname']> {
apiData?: PageProps<Pathname>['apiData']; apiData?: PageProps<Pathname>['apiData'];
} }
initSentry();
const PageNextJs = <Pathname extends Route['pathname']>(props: Props<Pathname>) => { const PageNextJs = <Pathname extends Route['pathname']>(props: Props<Pathname>) => {
const { title, description, opengraph, canonical } = metadata.generate(props, props.apiData); const { title, description, opengraph, canonical } = metadata.generate(props, props.apiData);
......
...@@ -15,8 +15,8 @@ function generateCspPolicy() { ...@@ -15,8 +15,8 @@ function generateCspPolicy() {
descriptors.marketplace(), descriptors.marketplace(),
descriptors.mixpanel(), descriptors.mixpanel(),
descriptors.monaco(), descriptors.monaco(),
descriptors.rollbar(),
descriptors.safe(), descriptors.safe(),
descriptors.sentry(),
descriptors.usernameApi(), descriptors.usernameApi(),
descriptors.walletConnect(), descriptors.walletConnect(),
); );
......
...@@ -11,25 +11,6 @@ const MAIN_DOMAINS = [ ...@@ -11,25 +11,6 @@ const MAIN_DOMAINS = [
config.app.host, config.app.host,
].filter(Boolean); ].filter(Boolean);
const getCspReportUrl = () => {
try {
const sentryFeature = config.features.sentry;
if (!sentryFeature.isEnabled || !process.env.SENTRY_CSP_REPORT_URI) {
return;
}
const url = new URL(process.env.SENTRY_CSP_REPORT_URI);
// https://docs.sentry.io/product/security-policy-reporting/#additional-configuration
url.searchParams.set('sentry_environment', sentryFeature.environment);
sentryFeature.release && url.searchParams.set('sentry_release', sentryFeature.release);
return url.toString();
} catch (error) {
return;
}
};
const externalFontsDomains = (() => { const externalFontsDomains = (() => {
try { try {
return [ return [
...@@ -152,17 +133,5 @@ export function app(): CspDev.DirectiveDescriptor { ...@@ -152,17 +133,5 @@ export function app(): CspDev.DirectiveDescriptor {
// allow remix.ethereum.org to embed our contract page in iframe // allow remix.ethereum.org to embed our contract page in iframe
'remix.ethereum.org', 'remix.ethereum.org',
], ],
...((() => {
if (!config.features.sentry.isEnabled) {
return {};
}
return {
'report-uri': [
getCspReportUrl(),
].filter(Boolean),
};
})()),
}; };
} }
...@@ -10,7 +10,7 @@ export { helia } from './helia'; ...@@ -10,7 +10,7 @@ export { helia } from './helia';
export { marketplace } from './marketplace'; export { marketplace } from './marketplace';
export { mixpanel } from './mixpanel'; export { mixpanel } from './mixpanel';
export { monaco } from './monaco'; export { monaco } from './monaco';
export { rollbar } from './rollbar';
export { safe } from './safe'; export { safe } from './safe';
export { sentry } from './sentry';
export { usernameApi } from './usernameApi'; export { usernameApi } from './usernameApi';
export { walletConnect } from './walletConnect'; export { walletConnect } from './walletConnect';
import type CspDev from 'csp-dev'; import type CspDev from 'csp-dev';
export function sentry(): CspDev.DirectiveDescriptor { import config from 'configs/app';
export function rollbar(): CspDev.DirectiveDescriptor {
if (!config.features.rollbar.isEnabled) {
return {};
}
return { return {
'connect-src': [ 'connect-src': [
'sentry.io', 'api.rollbar.com',
'*.sentry.io',
], ],
}; };
} }
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';
import PageNextJs from 'nextjs/PageNextJs'; import PageNextJs from 'nextjs/PageNextJs';
import { useRollbar } from 'lib/rollbar';
import AppError from 'ui/shared/AppError/AppError'; import AppError from 'ui/shared/AppError/AppError';
import LayoutError from 'ui/shared/layout/LayoutError'; 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 = () => {
const rollbar = useRollbar();
React.useEffect(() => { React.useEffect(() => {
Sentry.captureException(new Error('Page not found'), { tags: { source: '404' } }); rollbar?.error('Page not found');
}, []); }, [ rollbar ]);
return ( return (
<PageNextJs pathname="/404"> <PageNextJs pathname="/404">
......
import { type ChakraProps } from '@chakra-ui/react'; import { type ChakraProps } from '@chakra-ui/react';
import { GrowthBookProvider } from '@growthbook/growthbook-react'; import { GrowthBookProvider } from '@growthbook/growthbook-react';
import * as Sentry from '@sentry/react';
import { QueryClientProvider } from '@tanstack/react-query'; import { QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import type { AppProps } from 'next/app'; import type { AppProps } from 'next/app';
...@@ -19,6 +18,7 @@ import { SettingsContextProvider } from 'lib/contexts/settings'; ...@@ -19,6 +18,7 @@ import { SettingsContextProvider } from 'lib/contexts/settings';
import { growthBook } from 'lib/growthbook/init'; import { growthBook } from 'lib/growthbook/init';
import useLoadFeatures from 'lib/growthbook/useLoadFeatures'; import useLoadFeatures from 'lib/growthbook/useLoadFeatures';
import useNotifyOnNavigation from 'lib/hooks/useNotifyOnNavigation'; import useNotifyOnNavigation from 'lib/hooks/useNotifyOnNavigation';
import { clientConfig as rollbarConfig, Provider as RollbarProvider } from 'lib/rollbar';
import { SocketProvider } from 'lib/socket/context'; import { SocketProvider } from 'lib/socket/context';
import RewardsLoginModal from 'ui/rewards/login/RewardsLoginModal'; import RewardsLoginModal from 'ui/rewards/login/RewardsLoginModal';
import AppErrorBoundary from 'ui/shared/AppError/AppErrorBoundary'; import AppErrorBoundary from 'ui/shared/AppError/AppErrorBoundary';
...@@ -53,17 +53,13 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) { ...@@ -53,17 +53,13 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) {
const queryClient = useQueryClientConfig(); const queryClient = useQueryClientConfig();
const handleError = React.useCallback((error: Error) => {
Sentry.captureException(error);
}, []);
const getLayout = Component.getLayout ?? ((page) => <Layout>{ page }</Layout>); const getLayout = Component.getLayout ?? ((page) => <Layout>{ page }</Layout>);
return ( return (
<ChakraProvider cookies={ pageProps.cookies }> <ChakraProvider cookies={ pageProps.cookies }>
<RollbarProvider config={ rollbarConfig }>
<AppErrorBoundary <AppErrorBoundary
{ ...ERROR_SCREEN_STYLES } { ...ERROR_SCREEN_STYLES }
onError={ handleError }
Container={ AppErrorGlobalContainer } Container={ AppErrorGlobalContainer }
> >
<Web3ModalProvider> <Web3ModalProvider>
...@@ -89,6 +85,7 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) { ...@@ -89,6 +85,7 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) {
</AppContextProvider> </AppContextProvider>
</Web3ModalProvider> </Web3ModalProvider>
</AppErrorBoundary> </AppErrorBoundary>
</RollbarProvider>
</ChakraProvider> </ChakraProvider>
); );
} }
......
...@@ -14,7 +14,6 @@ import { ...@@ -14,7 +14,6 @@ import {
IconButton, IconButton,
Skeleton, Skeleton,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import * as Sentry from '@sentry/react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import QRCode from 'qrcode'; import QRCode from 'qrcode';
import React from 'react'; import React from 'react';
...@@ -23,6 +22,7 @@ import type { Address as AddressType } from 'types/api/address'; ...@@ -23,6 +22,7 @@ import type { Address as AddressType } from 'types/api/address';
import getPageType from 'lib/mixpanel/getPageType'; import getPageType from 'lib/mixpanel/getPageType';
import * as mixpanel from 'lib/mixpanel/index'; import * as mixpanel from 'lib/mixpanel/index';
import { useRollbar } from 'lib/rollbar';
import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
...@@ -40,6 +40,7 @@ const AddressQrCode = ({ address, className, isLoading }: Props) => { ...@@ -40,6 +40,7 @@ const AddressQrCode = ({ address, className, isLoading }: Props) => {
const { isOpen, onOpen, onClose } = useDisclosure(); const { isOpen, onOpen, onClose } = useDisclosure();
const router = useRouter(); const router = useRouter();
const rollbar = useRollbar();
const [ qr, setQr ] = React.useState(''); const [ qr, setQr ] = React.useState('');
const [ error, setError ] = React.useState(''); const [ error, setError ] = React.useState('');
...@@ -51,7 +52,7 @@ const AddressQrCode = ({ address, className, isLoading }: Props) => { ...@@ -51,7 +52,7 @@ const AddressQrCode = ({ address, className, isLoading }: Props) => {
QRCode.toString(address.hash, SVG_OPTIONS, (error: Error | null | undefined, svg: string) => { QRCode.toString(address.hash, SVG_OPTIONS, (error: Error | null | undefined, svg: string) => {
if (error) { if (error) {
setError('We were unable to generate QR code.'); setError('We were unable to generate QR code.');
Sentry.captureException(error, { tags: { source: 'qr_code' } }); rollbar?.warn('QR code generation failed');
return; return;
} }
...@@ -60,7 +61,7 @@ const AddressQrCode = ({ address, className, isLoading }: Props) => { ...@@ -60,7 +61,7 @@ const AddressQrCode = ({ address, className, isLoading }: Props) => {
mixpanel.logEvent(mixpanel.EventTypes.QR_CODE, { 'Page type': pageType }); mixpanel.logEvent(mixpanel.EventTypes.QR_CODE, { 'Page type': pageType });
}); });
} }
}, [ address.hash, isOpen, onClose, pageType ]); }, [ address.hash, isOpen, onClose, pageType, rollbar ]);
if (isLoading) { if (isLoading) {
return <Skeleton className={ className } w="36px" h="32px" borderRadius="base"/>; return <Skeleton className={ className } w="36px" h="32px" borderRadius="base"/>;
......
import { VStack, Textarea, Button, Alert, AlertTitle, AlertDescription, Code, Flex, Box } from '@chakra-ui/react'; import { VStack, Textarea, Button, Alert, AlertTitle, AlertDescription, Code, Flex, Box } from '@chakra-ui/react';
import * as Sentry from '@sentry/react';
import mixpanel from 'mixpanel-browser'; import mixpanel from 'mixpanel-browser';
import type { ChangeEvent } from 'react'; import type { ChangeEvent } from 'react';
import React from 'react'; import React from 'react';
...@@ -9,12 +8,13 @@ import * as cookies from 'lib/cookies'; ...@@ -9,12 +8,13 @@ import * as cookies from 'lib/cookies';
import useFeatureValue from 'lib/growthbook/useFeatureValue'; import useFeatureValue from 'lib/growthbook/useFeatureValue';
import useGradualIncrement from 'lib/hooks/useGradualIncrement'; import useGradualIncrement from 'lib/hooks/useGradualIncrement';
import useToast from 'lib/hooks/useToast'; import useToast from 'lib/hooks/useToast';
import { useRollbar } from 'lib/rollbar';
import PageTitle from 'ui/shared/Page/PageTitle'; import PageTitle from 'ui/shared/Page/PageTitle';
const Login = () => { const Login = () => {
const rollbar = useRollbar();
const toast = useToast(); const toast = useToast();
const [ num, setNum ] = useGradualIncrement(0); const [ num, setNum ] = useGradualIncrement(0);
const testFeature = useFeatureValue('test_value', 'fallback'); const testFeature = useFeatureValue('test_value', 'fallback');
const [ isFormVisible, setFormVisibility ] = React.useState(false); const [ isFormVisible, setFormVisibility ] = React.useState(false);
...@@ -23,12 +23,12 @@ const Login = () => { ...@@ -23,12 +23,12 @@ const Login = () => {
React.useEffect(() => { React.useEffect(() => {
const token = cookies.get(cookies.NAMES.API_TOKEN); const token = cookies.get(cookies.NAMES.API_TOKEN);
setFormVisibility(Boolean(!token && config.features.account.isEnabled)); setFormVisibility(Boolean(!token && config.features.account.isEnabled));
// throw new Error('Test error'); // throw new Error('Render error');
}, []); }, []);
const checkSentry = React.useCallback(() => { const checkRollbar = React.useCallback(() => {
Sentry.captureException(new Error('Test error'), { tags: { source: 'test' } }); rollbar?.error('Test error', { payload: 'foo' });
}, []); }, [ rollbar ]);
const checkMixpanel = React.useCallback(() => { const checkMixpanel = React.useCallback(() => {
mixpanel.track('Test event', { my_prop: 'foo bar' }); mixpanel.track('Test event', { my_prop: 'foo bar' });
...@@ -80,7 +80,7 @@ const Login = () => { ...@@ -80,7 +80,7 @@ const Login = () => {
</> </>
) } ) }
<Flex columnGap={ 2 }> <Flex columnGap={ 2 }>
<Button colorScheme="red" onClick={ checkSentry }>Check Sentry</Button> <Button colorScheme="red" onClick={ checkRollbar }>Check Rollbar</Button>
<Button colorScheme="teal" onClick={ checkMixpanel }>Check Mixpanel</Button> <Button colorScheme="teal" onClick={ checkMixpanel }>Check Mixpanel</Button>
</Flex> </Flex>
<Flex columnGap={ 2 } alignItems="center"> <Flex columnGap={ 2 } alignItems="center">
......
import { chakra } from '@chakra-ui/react'; import { chakra } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import getErrorCauseStatusCode from 'lib/errors/getErrorCauseStatusCode';
import { useRollbar } from 'lib/rollbar';
import ErrorBoundary from 'ui/shared/ErrorBoundary'; import ErrorBoundary from 'ui/shared/ErrorBoundary';
import AppError from './AppError'; import AppError from './AppError';
...@@ -8,11 +10,12 @@ import AppError from './AppError'; ...@@ -8,11 +10,12 @@ import AppError from './AppError';
interface Props { interface Props {
className?: string; className?: string;
children: React.ReactNode; children: React.ReactNode;
onError?: (error: Error) => void;
Container?: React.FC<{ children: React.ReactNode }>; Container?: React.FC<{ children: React.ReactNode }>;
} }
const AppErrorBoundary = ({ className, children, onError, Container }: Props) => { const AppErrorBoundary = ({ className, children, Container }: Props) => {
const rollbar = useRollbar();
const renderErrorScreen = React.useCallback((error?: Error) => { const renderErrorScreen = React.useCallback((error?: Error) => {
const content = <AppError error={ error } className={ className }/>; const content = <AppError error={ error } className={ className }/>;
...@@ -22,8 +25,21 @@ const AppErrorBoundary = ({ className, children, onError, Container }: Props) => ...@@ -22,8 +25,21 @@ const AppErrorBoundary = ({ className, children, onError, Container }: Props) =>
return content; return content;
}, [ className, Container ]); }, [ className, Container ]);
const handleError = React.useCallback((error: Error) => {
const statusCode = getErrorCauseStatusCode(error);
if (statusCode || !rollbar) {
// For now, we are not interested in logging errors from the API.
// If an error from a resource should be logged, please consider passing "logError: true" to the useApiQuery or useApiFetch hook.
return;
}
// To this point, there can only be errors that lead to a page crash.
// Therefore, we set the error level to "critical."
rollbar.critical(error);
}, [ rollbar ]);
return ( return (
<ErrorBoundary renderErrorScreen={ renderErrorScreen } onError={ onError }> <ErrorBoundary renderErrorScreen={ renderErrorScreen } onError={ handleError }>
{ children } { children }
</ErrorBoundary> </ErrorBoundary>
); );
......
import * as Sentry from '@sentry/react';
import React from 'react'; import React from 'react';
import * as cookies from 'lib/cookies'; import * as cookies from 'lib/cookies';
import { useRollbar } from 'lib/rollbar';
import useProfileQuery from 'ui/snippets/auth/useProfileQuery'; import useProfileQuery from 'ui/snippets/auth/useProfileQuery';
export default function useRedirectForInvalidAuthToken() { export default function useRedirectForInvalidAuthToken() {
const rollbar = useRollbar();
const profileQuery = useProfileQuery(); const profileQuery = useProfileQuery();
const errorStatus = profileQuery.error?.status; const errorStatus = profileQuery.error?.status;
...@@ -13,10 +14,9 @@ export default function useRedirectForInvalidAuthToken() { ...@@ -13,10 +14,9 @@ export default function useRedirectForInvalidAuthToken() {
const apiToken = cookies.get(cookies.NAMES.API_TOKEN); const apiToken = cookies.get(cookies.NAMES.API_TOKEN);
if (apiToken) { if (apiToken) {
Sentry.captureException(new Error('Invalid API token'), { tags: { source: 'invalid_api_token' } });
cookies.remove(cookies.NAMES.API_TOKEN); cookies.remove(cookies.NAMES.API_TOKEN);
window.location.assign('/'); window.location.assign('/');
} }
} }
}, [ errorStatus ]); }, [ errorStatus, rollbar ]);
} }
...@@ -5415,6 +5415,13 @@ ...@@ -5415,6 +5415,13 @@
prop-types "^15.7.2" prop-types "^15.7.2"
tslib "^2.3.0" tslib "^2.3.0"
"@rollbar/react@0.12.0-beta":
version "0.12.0-beta"
resolved "https://registry.yarnpkg.com/@rollbar/react/-/react-0.12.0-beta.tgz#b80dfab1535ece358a0e98a4f26aadee8b54edda"
integrity sha512-8udBX0lJwdBBq+O/jqDXpg/giHt8bo/Us1IlTkHEdCBO18Cjj7sxWJ80OPFxiPRNwZgZnhf2HbxQxvLN+4FeJA==
dependencies:
tiny-invariant "^1.1.0"
"@rollup/pluginutils@^5.0.0": "@rollup/pluginutils@^5.0.0":
version "5.0.2" version "5.0.2"
resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-5.0.2.tgz#012b8f53c71e4f6f9cb317e311df1404f56e7a33" resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-5.0.2.tgz#012b8f53c71e4f6f9cb317e311df1404f56e7a33"
...@@ -5600,70 +5607,6 @@ ...@@ -5600,70 +5607,6 @@
"@noble/hashes" "~1.5.0" "@noble/hashes" "~1.5.0"
"@scure/base" "~1.1.8" "@scure/base" "~1.1.8"
"@sentry/browser@7.24.0":
version "7.24.0"
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-7.24.0.tgz#3e657f8032322d07f98486f44e048fcdbc3aaff7"
integrity sha512-G2eeYWF7rIz1Dm8PCQiuuTzctrrJSWqD5MCWOICqHr8FrxOPVsVO3Qa/B7/+BSpHd+V7sSUU17oFu41SAWZCOg==
dependencies:
"@sentry/core" "7.24.0"
"@sentry/types" "7.24.0"
"@sentry/utils" "7.24.0"
tslib "^1.9.3"
"@sentry/cli@^2.21.2":
version "2.21.2"
resolved "https://registry.yarnpkg.com/@sentry/cli/-/cli-2.21.2.tgz#89e5633ff48a83d078c76c6997fffd4b68b2da1c"
integrity sha512-X1nye89zl+QV3FSuQDGItfM51tW9PQ7ce0TtV/12DgGgTVEgnVp5uvO3wX5XauHvulQzRPzwUL3ZK+yS5bAwCw==
dependencies:
https-proxy-agent "^5.0.0"
node-fetch "^2.6.7"
progress "^2.0.3"
proxy-from-env "^1.1.0"
which "^2.0.2"
"@sentry/core@7.24.0":
version "7.24.0"
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.24.0.tgz#e856a071c702279854d6e5221957d61cd48036cc"
integrity sha512-QVRtmnaWEI0/MHIfBozgsMfh+7WU6OfpvUd72x1Dpk3Zk6Zs7Hqq0YfxfeBd7ApjNjGogPl1beaHcHAHlr3IyA==
dependencies:
"@sentry/types" "7.24.0"
"@sentry/utils" "7.24.0"
tslib "^1.9.3"
"@sentry/react@7.24.0":
version "7.24.0"
resolved "https://registry.yarnpkg.com/@sentry/react/-/react-7.24.0.tgz#aa51b09bad8c5d7af163a28a4718abb1e68ea232"
integrity sha512-uW6j7wrPxg7UillUVdsjrsIPqqPEAN/PdotR2O75Qh7s0aR7+Le8i29C4VeW/iubXhlxF/qFvnXwqG5Ji4GwBg==
dependencies:
"@sentry/browser" "7.24.0"
"@sentry/types" "7.24.0"
"@sentry/utils" "7.24.0"
hoist-non-react-statics "^3.3.2"
tslib "^1.9.3"
"@sentry/tracing@7.24.0":
version "7.24.0"
resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-7.24.0.tgz#d9477a2da9c902ca9663cd4cd87ba279f73f212c"
integrity sha512-O/4hE/lYM6+hM4K5O06pWNuHzZb2ex5f2497bxkwxq17wpJhVp1oDuz8hbWNL7BUNeIbeDpqunBHQzzmVRbL1Q==
dependencies:
"@sentry/core" "7.24.0"
"@sentry/types" "7.24.0"
"@sentry/utils" "7.24.0"
tslib "^1.9.3"
"@sentry/types@7.24.0":
version "7.24.0"
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.24.0.tgz#1acc841efe7bd56fc3eb647a613dba92631e8413"
integrity sha512-Xs4r9esBPieJUA6cGmMqfSQiinILdlhScjM+NqDSzxOo8+LRCJzckTLhUttBGVlaAoa4hjCEsfkHA1tVV1DycA==
"@sentry/utils@7.24.0":
version "7.24.0"
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.24.0.tgz#73f100dc8942e73353473eaf3168e5052e2f45be"
integrity sha512-baaRDhHWHTyhmR6V8YKSo0NvN+D17pIKRDmb2vpWHVpTjobKCivNBLRoy3VhnIMS/24XyZnL028QLwkUNLg1Ug==
dependencies:
"@sentry/types" "7.24.0"
tslib "^1.9.3"
"@sideway/address@^4.1.3": "@sideway/address@^4.1.3":
version "4.1.4" version "4.1.4"
resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.4.tgz#03dccebc6ea47fdc226f7d3d1ad512955d4783f0" resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.4.tgz#03dccebc6ea47fdc226f7d3d1ad512955d4783f0"
...@@ -8270,6 +8213,11 @@ async-mutex@^0.2.6: ...@@ -8270,6 +8213,11 @@ async-mutex@^0.2.6:
dependencies: dependencies:
tslib "^2.0.0" tslib "^2.0.0"
async@~3.2.3:
version "3.2.6"
resolved "https://registry.yarnpkg.com/async/-/async-3.2.6.tgz#1b0728e14929d51b85b449b7f06e27c1145e38ce"
integrity sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==
asynckit@^0.4.0: asynckit@^0.4.0:
version "0.4.0" version "0.4.0"
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
...@@ -9015,6 +8963,11 @@ consola@^3.2.3: ...@@ -9015,6 +8963,11 @@ consola@^3.2.3:
resolved "https://registry.yarnpkg.com/consola/-/consola-3.2.3.tgz#0741857aa88cfa0d6fd53f1cff0375136e98502f" resolved "https://registry.yarnpkg.com/consola/-/consola-3.2.3.tgz#0741857aa88cfa0d6fd53f1cff0375136e98502f"
integrity sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ== integrity sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ==
console-polyfill@0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/console-polyfill/-/console-polyfill-0.3.0.tgz#84900902a18c47a5eba932be75fa44d23e8af861"
integrity sha512-w+JSDZS7XML43Xnwo2x5O5vxB0ID7T5BdqDtyqT6uiCAX2kZAgcWxNaGqT97tZfSHzfOcvrfsDAodKcJ3UvnXQ==
convert-hrtime@^5.0.0: convert-hrtime@^5.0.0:
version "5.0.0" version "5.0.0"
resolved "https://registry.yarnpkg.com/convert-hrtime/-/convert-hrtime-5.0.0.tgz#f2131236d4598b95de856926a67100a0a97e9fa3" resolved "https://registry.yarnpkg.com/convert-hrtime/-/convert-hrtime-5.0.0.tgz#f2131236d4598b95de856926a67100a0a97e9fa3"
...@@ -9610,6 +9563,13 @@ debug@^3.2.7: ...@@ -9610,6 +9563,13 @@ debug@^3.2.7:
dependencies: dependencies:
ms "^2.1.1" ms "^2.1.1"
decache@^3.0.5:
version "3.1.0"
resolved "https://registry.yarnpkg.com/decache/-/decache-3.1.0.tgz#4f5036fbd6581fcc97237ac3954a244b9536c2da"
integrity sha512-p7D6wJ5EJFFq1CcF2lu1XeqKFLBob8jRQGNAvFLTsV3CbSKBl3VtliAVlUIGz2i9H6kEFnI2Amaft5ZopIG2Fw==
dependencies:
find "^0.2.4"
decamelize@^1.2.0: decamelize@^1.2.0:
version "1.2.0" version "1.2.0"
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
...@@ -10052,6 +10012,13 @@ error-ex@^1.3.1: ...@@ -10052,6 +10012,13 @@ error-ex@^1.3.1:
dependencies: dependencies:
is-arrayish "^0.2.1" is-arrayish "^0.2.1"
error-stack-parser@^2.0.4:
version "2.1.4"
resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.1.4.tgz#229cb01cdbfa84440bfa91876285b94680188286"
integrity sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==
dependencies:
stackframe "^1.3.4"
error@7.0.2: error@7.0.2:
version "7.0.2" version "7.0.2"
resolved "https://registry.yarnpkg.com/error/-/error-7.0.2.tgz#a5f75fff4d9926126ddac0ea5dc38e689153cb02" resolved "https://registry.yarnpkg.com/error/-/error-7.0.2.tgz#a5f75fff4d9926126ddac0ea5dc38e689153cb02"
...@@ -10959,6 +10926,13 @@ find-yarn-workspace-root@^2.0.0: ...@@ -10959,6 +10926,13 @@ find-yarn-workspace-root@^2.0.0:
dependencies: dependencies:
micromatch "^4.0.2" micromatch "^4.0.2"
find@^0.2.4:
version "0.2.9"
resolved "https://registry.yarnpkg.com/find/-/find-0.2.9.tgz#4b73f1ff9e56ad91b76e716407fe5ffe6554bb8c"
integrity sha512-7a4/LCiInB9xYMnAUEjLilL9FKclwbwK7VlXw+h5jMvT2TDFeYFCHM24O1XdnC/on/hx8mxVO3FTQkyHZnOghQ==
dependencies:
traverse-chain "~0.1.0"
flat-cache@^4.0.0: flat-cache@^4.0.0:
version "4.0.1" version "4.0.1"
resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-4.0.1.tgz#0ece39fcb14ee012f4b0410bd33dd9c1f011127c" resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-4.0.1.tgz#0ece39fcb14ee012f4b0410bd33dd9c1f011127c"
...@@ -11658,7 +11632,7 @@ http-shutdown@^1.2.2: ...@@ -11658,7 +11632,7 @@ http-shutdown@^1.2.2:
resolved "https://registry.yarnpkg.com/http-shutdown/-/http-shutdown-1.2.2.tgz#41bc78fc767637c4c95179bc492f312c0ae64c5f" resolved "https://registry.yarnpkg.com/http-shutdown/-/http-shutdown-1.2.2.tgz#41bc78fc767637c4c95179bc492f312c0ae64c5f"
integrity sha512-S9wWkJ/VSY9/k4qcjG318bqJNruzE4HySUhFYknwmu6LBP97KLLfwNf+n4V1BHurvFNkSKLFnK/RsuUnRTf9Vw== integrity sha512-S9wWkJ/VSY9/k4qcjG318bqJNruzE4HySUhFYknwmu6LBP97KLLfwNf+n4V1BHurvFNkSKLFnK/RsuUnRTf9Vw==
https-proxy-agent@^5.0.0, https-proxy-agent@^5.0.1: https-proxy-agent@^5.0.1:
version "5.0.1" version "5.0.1"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6"
integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==
...@@ -13158,6 +13132,11 @@ json-stable-stringify@^1.0.2: ...@@ -13158,6 +13132,11 @@ json-stable-stringify@^1.0.2:
dependencies: dependencies:
jsonify "^0.0.1" jsonify "^0.0.1"
json-stringify-safe@~5.0.0:
version "5.0.1"
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==
json5@^1.0.2: json5@^1.0.2:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593"
...@@ -13536,6 +13515,11 @@ lru-cache@^6.0.0: ...@@ -13536,6 +13515,11 @@ lru-cache@^6.0.0:
dependencies: dependencies:
yallist "^4.0.0" yallist "^4.0.0"
lru-cache@~2.2.1:
version "2.2.4"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.2.4.tgz#6c658619becf14031d0d0b594b16042ce4dc063d"
integrity sha512-Q5pAgXs+WEAfoEdw2qKQhNFFhMoFMTYqRVKKUMnzuiR7oKFHS7fWo848cPcTKw+4j/IdN17NyzdhVKgabFV0EA==
lz-string@^1.5.0: lz-string@^1.5.0:
version "1.5.0" version "1.5.0"
resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.5.0.tgz#c1ab50f77887b712621201ba9fd4e3a6ed099941" resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.5.0.tgz#c1ab50f77887b712621201ba9fd4e3a6ed099941"
...@@ -14920,11 +14904,6 @@ progress-events@^1.0.0, progress-events@^1.0.1: ...@@ -14920,11 +14904,6 @@ progress-events@^1.0.0, progress-events@^1.0.1:
resolved "https://registry.yarnpkg.com/progress-events/-/progress-events-1.0.1.tgz#693b6d4153f08c1418ae3cd5fcad8596c91db7e8" resolved "https://registry.yarnpkg.com/progress-events/-/progress-events-1.0.1.tgz#693b6d4153f08c1418ae3cd5fcad8596c91db7e8"
integrity sha512-MOzLIwhpt64KIVN64h1MwdKWiyKFNc/S6BoYKPIVUHFg0/eIEyBulhWCgn678v/4c0ri3FdGuzXymNCv02MUIw== integrity sha512-MOzLIwhpt64KIVN64h1MwdKWiyKFNc/S6BoYKPIVUHFg0/eIEyBulhWCgn678v/4c0ri3FdGuzXymNCv02MUIw==
progress@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
prom-client@15.1.1: prom-client@15.1.1:
version "15.1.1" version "15.1.1"
resolved "https://registry.yarnpkg.com/prom-client/-/prom-client-15.1.1.tgz#71ba84371241acd173181b04a436782c246f3652" resolved "https://registry.yarnpkg.com/prom-client/-/prom-client-15.1.1.tgz#71ba84371241acd173181b04a436782c246f3652"
...@@ -15658,6 +15637,11 @@ repeat-string@^1.5.2: ...@@ -15658,6 +15637,11 @@ repeat-string@^1.5.2:
resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637"
integrity sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w== integrity sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==
request-ip@~3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/request-ip/-/request-ip-3.3.0.tgz#863451e8fec03847d44f223e30a5d63e369fa611"
integrity sha512-cA6Xh6e0fDBBBwH77SLJaJPBmD3nWVAcF9/XAcsrIHdjhFzFiB5aNQFytdjCGPezU3ROwrR11IddKAM08vohxA==
require-directory@^2.1.1: require-directory@^2.1.1:
version "2.1.1" version "2.1.1"
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
...@@ -15780,6 +15764,21 @@ robust-predicates@^3.0.0: ...@@ -15780,6 +15764,21 @@ robust-predicates@^3.0.0:
resolved "https://registry.yarnpkg.com/robust-predicates/-/robust-predicates-3.0.1.tgz#ecde075044f7f30118682bd9fb3f123109577f9a" resolved "https://registry.yarnpkg.com/robust-predicates/-/robust-predicates-3.0.1.tgz#ecde075044f7f30118682bd9fb3f123109577f9a"
integrity sha512-ndEIpszUHiG4HtDsQLeIuMvRsDnn8c8rYStabochtUeCvfuvNptb5TUbVD68LRAILPX7p9nqQGh4xJgn3EHS/g== integrity sha512-ndEIpszUHiG4HtDsQLeIuMvRsDnn8c8rYStabochtUeCvfuvNptb5TUbVD68LRAILPX7p9nqQGh4xJgn3EHS/g==
rollbar@2.26.4:
version "2.26.4"
resolved "https://registry.yarnpkg.com/rollbar/-/rollbar-2.26.4.tgz#05e47d3b1f52ab6da9f88710ec66371a76cdc3c9"
integrity sha512-JKmrj6riYm9ZPJisgxljgH4uCsvjMHDHXrinDF7aAFaP+eoF51HomVPtLcDTYLsrJ568aKVNLUhedFajONBwSg==
dependencies:
async "~3.2.3"
console-polyfill "0.3.0"
error-stack-parser "^2.0.4"
json-stringify-safe "~5.0.0"
lru-cache "~2.2.1"
request-ip "~3.3.0"
source-map "^0.5.7"
optionalDependencies:
decache "^3.0.5"
rollup-plugin-visualizer@^5.9.2: rollup-plugin-visualizer@^5.9.2:
version "5.12.0" version "5.12.0"
resolved "https://registry.yarnpkg.com/rollup-plugin-visualizer/-/rollup-plugin-visualizer-5.12.0.tgz#661542191ce78ee4f378995297260d0c1efb1302" resolved "https://registry.yarnpkg.com/rollup-plugin-visualizer/-/rollup-plugin-visualizer-5.12.0.tgz#661542191ce78ee4f378995297260d0c1efb1302"
...@@ -16249,6 +16248,11 @@ stack-utils@^2.0.3: ...@@ -16249,6 +16248,11 @@ stack-utils@^2.0.3:
dependencies: dependencies:
escape-string-regexp "^2.0.0" escape-string-regexp "^2.0.0"
stackframe@^1.3.4:
version "1.3.4"
resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.3.4.tgz#b881a004c8c149a5e8efef37d51b16e412943310"
integrity sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==
stampit@^4.3.2: stampit@^4.3.2:
version "4.3.2" version "4.3.2"
resolved "https://registry.yarnpkg.com/stampit/-/stampit-4.3.2.tgz#cfd3f607dd628a161ce6305621597994b4d56573" resolved "https://registry.yarnpkg.com/stampit/-/stampit-4.3.2.tgz#cfd3f607dd628a161ce6305621597994b4d56573"
...@@ -16823,6 +16827,11 @@ tiny-invariant@^1.0.6: ...@@ -16823,6 +16827,11 @@ tiny-invariant@^1.0.6:
resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.3.1.tgz#8560808c916ef02ecfd55e66090df23a4b7aa642" resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.3.1.tgz#8560808c916ef02ecfd55e66090df23a4b7aa642"
integrity sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw== integrity sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==
tiny-invariant@^1.1.0:
version "1.3.3"
resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.3.3.tgz#46680b7a873a0d5d10005995eb90a70d74d60127"
integrity sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==
tiny-parse-argv@^2.2.0: tiny-parse-argv@^2.2.0:
version "2.4.0" version "2.4.0"
resolved "https://registry.yarnpkg.com/tiny-parse-argv/-/tiny-parse-argv-2.4.0.tgz#8612163a88104a5af9a64e4775cd1e091d4fa265" resolved "https://registry.yarnpkg.com/tiny-parse-argv/-/tiny-parse-argv-2.4.0.tgz#8612163a88104a5af9a64e4775cd1e091d4fa265"
...@@ -16889,6 +16898,11 @@ tr46@~0.0.3: ...@@ -16889,6 +16898,11 @@ tr46@~0.0.3:
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==
traverse-chain@~0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/traverse-chain/-/traverse-chain-0.1.0.tgz#61dbc2d53b69ff6091a12a168fd7d433107e40f1"
integrity sha512-up6Yvai4PYKhpNp5PkYtx50m3KbwQrqDwbuZP/ItyL64YEWHAvH6Md83LFLV/GRSk/BoUVwwgUzX6SOQSbsfAg==
traverse@~0.6.6: traverse@~0.6.6:
version "0.6.7" version "0.6.7"
resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.6.7.tgz#46961cd2d57dd8706c36664acde06a248f1173fe" resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.6.7.tgz#46961cd2d57dd8706c36664acde06a248f1173fe"
...@@ -16974,7 +16988,7 @@ tsconfig-paths@^3.15.0: ...@@ -16974,7 +16988,7 @@ tsconfig-paths@^3.15.0:
minimist "^1.2.6" minimist "^1.2.6"
strip-bom "^3.0.0" strip-bom "^3.0.0"
tslib@1.14.1, tslib@^1.8.1, tslib@^1.9.3: tslib@1.14.1, tslib@^1.8.1:
version "1.14.1" version "1.14.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
...@@ -17682,7 +17696,7 @@ which-typed-array@^1.1.2, which-typed-array@^1.1.8, which-typed-array@^1.1.9: ...@@ -17682,7 +17696,7 @@ which-typed-array@^1.1.2, which-typed-array@^1.1.8, which-typed-array@^1.1.9:
has-tostringtag "^1.0.0" has-tostringtag "^1.0.0"
is-typed-array "^1.1.10" is-typed-array "^1.1.10"
which@^2.0.1, which@^2.0.2: which@^2.0.1:
version "2.0.2" version "2.0.2"
resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
......
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