feat: maximize Sentry free plan coverage for frontend and backend

- ErrorBoundary: use Sentry.ErrorBoundary with crash feedback dialog
- RouteError: capture route errors in Sentry (skip chunk load errors)
- User context: set Sentry user on login (frontend + backend)
- Backend: enable profiling (profiles_sample_rate)
- Frontend: add feedback integration, lower replay rate to conserve quota
- Add temporary verification message for production validation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
chihlasm
2026-03-09 00:03:29 -04:00
parent 2a2894496d
commit 4d2f644bac
6 changed files with 77 additions and 52 deletions

View File

@@ -1,60 +1,55 @@
import { Component, type ReactNode } from 'react'
import * as Sentry from '@sentry/react'
import { type ReactNode } from 'react'
import { Button } from '@/components/ui/Button'
interface FallbackProps {
error: Error
resetError: () => void
}
function DefaultFallback({ error, resetError }: FallbackProps) {
return (
<div className="flex min-h-[400px] flex-col items-center justify-center p-8">
<div className="max-w-md text-center">
<h2 className="mb-2 text-xl font-semibold text-red-400">
Something went wrong
</h2>
<p className="mb-4 text-muted-foreground">
An unexpected error occurred. Please try refreshing the page.
</p>
<pre className="mb-4 overflow-auto rounded-xl bg-white/5 border border-border p-3 text-left text-xs text-red-400">
{error.message}
</pre>
<div className="flex justify-center gap-3">
<Button variant="secondary" onClick={resetError}>
Try Again
</Button>
<Button onClick={() => window.location.reload()}>
Refresh Page
</Button>
</div>
</div>
</div>
)
}
interface Props {
children: ReactNode
fallback?: ReactNode
}
interface State {
hasError: boolean
error: Error | null
}
export class ErrorBoundary extends Component<Props, State> {
constructor(props: Props) {
super(props)
this.state = { hasError: false, error: null }
}
static getDerivedStateFromError(error: Error): State {
return { hasError: true, error }
}
componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
console.error('ErrorBoundary caught an error:', error, errorInfo)
}
render() {
if (this.state.hasError) {
if (this.props.fallback) {
return this.props.fallback
}
return (
<div className="flex min-h-[400px] flex-col items-center justify-center p-8">
<div className="max-w-md text-center">
<h2 className="mb-2 text-xl font-semibold text-red-400">
Something went wrong
</h2>
<p className="mb-4 text-muted-foreground">
An unexpected error occurred. Please try refreshing the page.
</p>
{this.state.error && (
<pre className="mb-4 overflow-auto rounded-xl bg-white/5 border border-border p-3 text-left text-xs text-red-400">
{this.state.error.message}
</pre>
)}
<Button onClick={() => window.location.reload()}>
Refresh Page
</Button>
</div>
</div>
)
}
return this.props.children
}
export function ErrorBoundary({ children, fallback }: Props) {
return (
<Sentry.ErrorBoundary
fallback={({ error, resetError }) => {
if (fallback) return fallback as React.ReactElement
return <DefaultFallback error={error as Error} resetError={resetError} />
}}
showDialog
>
{children}
</Sentry.ErrorBoundary>
)
}
export default ErrorBoundary

View File

@@ -1,5 +1,6 @@
import { useEffect } from 'react'
import { useRouteError, isRouteErrorResponse, useNavigate } from 'react-router-dom'
import * as Sentry from '@sentry/react'
import { Button } from '@/components/ui/Button'
function isChunkLoadError(error: unknown): boolean {
@@ -19,6 +20,13 @@ export function RouteError() {
const error = useRouteError()
const navigate = useNavigate()
// Report route errors to Sentry (skip chunk load errors — those are deploy artifacts)
useEffect(() => {
if (error && !isChunkLoadError(error)) {
Sentry.captureException(error)
}
}, [error])
// Auto-reload once on chunk load failures (stale deploy)
useEffect(() => {
if (isChunkLoadError(error)) {