diff --git a/.env.production b/.env.production new file mode 100644 index 000000000..cb4433f4e --- /dev/null +++ b/.env.production @@ -0,0 +1,4 @@ +# Sentry DSN — get from sentry.io → Project Settings → Client Keys +# VITE_SENTRY_DSN=https://xxx@oXXX.ingest.sentry.io/YYYY +VITE_SENTRY_DSN= +VITE_APP_VERSION=lotus diff --git a/package-lock.json b/package-lock.json index 5fb4a24c1..9bf8c719f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,6 +7,7 @@ "": { "name": "lotus-chat", "version": "4.12.1-lotus", + "hasInstallScript": true, "license": "AGPL-3.0-only", "dependencies": { "@atlaskit/pragmatic-drag-and-drop": "1.1.6", @@ -16,6 +17,7 @@ "@giphy/js-fetch-api": "5.8.0", "@giphy/js-types": "5.1.0", "@giphy/react-components": "10.1.2", + "@sentry/react": "10.53.1", "@tanstack/react-query": "5.24.1", "@tanstack/react-query-devtools": "5.24.1", "@tanstack/react-virtual": "3.2.0", @@ -5375,6 +5377,97 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@sentry-internal/browser-utils": { + "version": "10.53.1", + "resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-10.53.1.tgz", + "integrity": "sha512-X4d6y8sBMjmNhcDW4eMBU3ASsNIMz8dqaFkhyIMN/dkYr/yZKnbRZPaVuVUGvHKjnlficPpIH0/HK9KBjrYxPw==", + "license": "MIT", + "dependencies": { + "@sentry/core": "10.53.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@sentry-internal/feedback": { + "version": "10.53.1", + "resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-10.53.1.tgz", + "integrity": "sha512-vVpTI/aEYN5d9IgZeYJWMqVaN0+iFgidSrYNAsZTh1US5sJUzF/wrl+68KdpmCtFROrN3jiAn1oPSwL5CKvEJA==", + "license": "MIT", + "dependencies": { + "@sentry/core": "10.53.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@sentry-internal/replay": { + "version": "10.53.1", + "resolved": "https://registry.npmjs.org/@sentry-internal/replay/-/replay-10.53.1.tgz", + "integrity": "sha512-wZNzTBYkgGUPWMuUQv7L64+OJmoCnz7GQNiTrTFK6EVAjJXFBCSsPp/nhif0bLhbk8+0g4xz633uOhpXuQbFdw==", + "license": "MIT", + "dependencies": { + "@sentry-internal/browser-utils": "10.53.1", + "@sentry/core": "10.53.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@sentry-internal/replay-canvas": { + "version": "10.53.1", + "resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-10.53.1.tgz", + "integrity": "sha512-aueLaf/2prExwA76BGU5/bOXCKWqtt6jQXWA6WJQNrmKpPEtZJB4ypnpsou0McXQCF8tur2Y8U0TEkwQP13yJQ==", + "license": "MIT", + "dependencies": { + "@sentry-internal/replay": "10.53.1", + "@sentry/core": "10.53.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@sentry/browser": { + "version": "10.53.1", + "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-10.53.1.tgz", + "integrity": "sha512-zXF373hzUOGzUOrqd8xb1U3LQi5uYC3mwv+z5OMKUUinQlu30tTWBs7ypy6YTchtix9QlYaHWlayUF8vBZ5UjA==", + "license": "MIT", + "dependencies": { + "@sentry-internal/browser-utils": "10.53.1", + "@sentry-internal/feedback": "10.53.1", + "@sentry-internal/replay": "10.53.1", + "@sentry-internal/replay-canvas": "10.53.1", + "@sentry/core": "10.53.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@sentry/core": { + "version": "10.53.1", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-10.53.1.tgz", + "integrity": "sha512-XG4ezlkyuAPjBC5+9kXC94rXXuqYTw9NRhfaDHssbTFaGnqBR8vQX2UUgZfY7ucbeelRDGfBu1sywoU+mB04uA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@sentry/react": { + "version": "10.53.1", + "resolved": "https://registry.npmjs.org/@sentry/react/-/react-10.53.1.tgz", + "integrity": "sha512-lrwNq5T/zW84l60894TpKHPcvFuc1I/Hnohecc0TfYVpIcYYuw2orCHoU4v4wgkFaJUpegVetbgdOphViyLVjA==", + "license": "MIT", + "dependencies": { + "@sentry/browser": "10.53.1", + "@sentry/core": "10.53.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "react": "^16.14.0 || 17.x || 18.x || 19.x" + } + }, "node_modules/@simple-libs/stream-utils": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@simple-libs/stream-utils/-/stream-utils-1.2.0.tgz", diff --git a/package.json b/package.json index 80b4bb556..5054afe0b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "lotus-chat", "version": "4.12.1-lotus", - "description": "Lotus Chat \u2014 Matrix client for Lotus Guild", + "description": "Lotus Chat — Matrix client for Lotus Guild", "main": "index.js", "type": "module", "engines": { @@ -70,6 +70,7 @@ "@giphy/js-fetch-api": "5.8.0", "@giphy/js-types": "5.1.0", "@giphy/react-components": "10.1.2", + "@sentry/react": "10.53.1", "@tanstack/react-query": "5.24.1", "@tanstack/react-query-devtools": "5.24.1", "@tanstack/react-virtual": "3.2.0", diff --git a/src/app/pages/App.tsx b/src/app/pages/App.tsx index 52ec7f206..45d474795 100644 --- a/src/app/pages/App.tsx +++ b/src/app/pages/App.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import * as Sentry from '@sentry/react'; import { Provider as JotaiProvider } from 'jotai'; import { OverlayContainerProvider, PopOutContainerProvider, TooltipContainerProvider } from 'folds'; import { RouterProvider } from 'react-router-dom'; @@ -22,33 +23,71 @@ function App() { const portalContainer = document.getElementById('portalContainer') ?? undefined; return ( - - - - - - } - error={(err, retry, ignore) => ( - - )} - > - {(clientConfig) => ( - - - - - - - - - )} - - - - - - + ( + + Something went wrong + + {error instanceof Error ? error.message : 'An unexpected error occurred.'} + + + Try again + + + )} + > + + + + + + } + error={(err, retry, ignore) => ( + + )} + > + {(clientConfig) => ( + + + + + + + + + )} + + + + + + + ); } diff --git a/src/index.tsx b/src/index.tsx index 6cc0ca38f..b7cb403d9 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,4 +1,5 @@ /* eslint-disable import/first */ +import * as Sentry from '@sentry/react'; import React from 'react'; import { createRoot } from 'react-dom/client'; import { enableMapSet } from 'immer'; @@ -6,6 +7,25 @@ import '@fontsource/inter/variable.css'; import 'folds/dist/style.css'; import { configClass, varsClass } from 'folds'; +if (import.meta.env.VITE_SENTRY_DSN) { + Sentry.init({ + dsn: import.meta.env.VITE_SENTRY_DSN, + environment: import.meta.env.MODE, + release: import.meta.env.VITE_APP_VERSION, + tracesSampleRate: 0.05, + // Don't send PII — strip user IPs and don't attach user info automatically + sendDefaultPii: false, + beforeSend(event) { + // Scrub any accessToken that may have leaked into breadcrumbs/data + const str = JSON.stringify(event); + if (str.includes('access_token') || str.includes('accessToken')) { + return null; + } + return event; + }, + }); +} + enableMapSet(); import './index.css';
+ {error instanceof Error ? error.message : 'An unexpected error occurred.'} +