Added `ignoreErrors: [/invalid origin/i]` to the Sentry `init()` configuration in `SentryInitEffect.tsx`. This filters out the "invalid origin" error from reCAPTCHA at the Sentry SDK level, preventing it from being captured even if the `.catch()` handler is somehow bypassed (e.g., by the periodic token refresh interval in `CaptchaProviderScriptLoaderEffect`). This is a defense-in-depth measure — the code fix prevents the error from becoming an unhandled rejection, and this monitoring fix ensures any residual captures are filtered.
117 lines
3.8 KiB
TypeScript
117 lines
3.8 KiB
TypeScript
import { currentUserState } from '@/auth/states/currentUserState';
|
|
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
|
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
|
import { sentryConfigState } from '@/client-config/states/sentryConfigState';
|
|
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
|
import { useEffect, useState } from 'react';
|
|
import { isNonEmptyString } from '@sniptt/guards';
|
|
import { isDefined } from 'twenty-shared/utils';
|
|
import { REACT_APP_SERVER_BASE_URL } from '~/config';
|
|
|
|
export const SentryInitEffect = () => {
|
|
const sentryConfig = useAtomStateValue(sentryConfigState);
|
|
|
|
const currentUser = useAtomStateValue(currentUserState);
|
|
const currentWorkspace = useAtomStateValue(currentWorkspaceState);
|
|
const currentWorkspaceMember = useAtomStateValue(currentWorkspaceMemberState);
|
|
|
|
const [isSentryInitialized, setIsSentryInitialized] = useState(false);
|
|
const [isSentryInitializing, setIsSentryInitializing] = useState(false);
|
|
const [isSentryUserDefined, setIsSentryUserDefined] = useState(false);
|
|
|
|
useEffect(() => {
|
|
const initializeSentry = async () => {
|
|
if (
|
|
isNonEmptyString(sentryConfig?.dsn) &&
|
|
!isSentryInitialized &&
|
|
!isSentryInitializing
|
|
) {
|
|
setIsSentryInitializing(true);
|
|
|
|
try {
|
|
const {
|
|
init,
|
|
browserTracingIntegration,
|
|
replayIntegration,
|
|
globalHandlersIntegration,
|
|
} = await import('@sentry/react');
|
|
|
|
init({
|
|
environment: sentryConfig?.environment ?? undefined,
|
|
release: sentryConfig?.release ?? undefined,
|
|
dsn: sentryConfig?.dsn,
|
|
integrations: [
|
|
browserTracingIntegration({}),
|
|
replayIntegration(),
|
|
globalHandlersIntegration({
|
|
onunhandledrejection: false, // handled in PromiseRejectionEffect
|
|
}),
|
|
],
|
|
tracePropagationTargets: [
|
|
'localhost:3001',
|
|
REACT_APP_SERVER_BASE_URL,
|
|
],
|
|
ignoreErrors: [
|
|
// reCAPTCHA errors when the domain is not registered in the site key's allowed domains
|
|
/invalid origin/i,
|
|
],
|
|
tracesSampleRate: 1.0,
|
|
replaysSessionSampleRate: 0.1,
|
|
replaysOnErrorSampleRate: 1.0,
|
|
});
|
|
|
|
setIsSentryInitialized(true);
|
|
} catch (error) {
|
|
// oxlint-disable-next-line no-console
|
|
console.error('Failed to initialize Sentry:', error);
|
|
} finally {
|
|
setIsSentryInitializing(false);
|
|
}
|
|
}
|
|
};
|
|
|
|
const updateSentryUser = async () => {
|
|
if (
|
|
isSentryInitialized &&
|
|
isDefined(currentUser) &&
|
|
!isSentryUserDefined
|
|
) {
|
|
try {
|
|
const { setUser } = await import('@sentry/react');
|
|
setUser({
|
|
email: currentUser?.email,
|
|
id: currentUser?.id,
|
|
workspaceId: currentWorkspace?.id,
|
|
workspaceMemberId: currentWorkspaceMember?.id,
|
|
});
|
|
setIsSentryUserDefined(true);
|
|
} catch (error) {
|
|
// oxlint-disable-next-line no-console
|
|
console.error('Failed to set Sentry user:', error);
|
|
}
|
|
} else if (!isDefined(currentUser) && isSentryInitialized) {
|
|
try {
|
|
const { setUser } = await import('@sentry/react');
|
|
setUser(null);
|
|
} catch (error) {
|
|
// oxlint-disable-next-line no-console
|
|
console.error('Failed to clear Sentry user:', error);
|
|
}
|
|
}
|
|
};
|
|
|
|
initializeSentry();
|
|
updateSentryUser();
|
|
}, [
|
|
sentryConfig,
|
|
isSentryInitialized,
|
|
isSentryInitializing,
|
|
currentUser,
|
|
currentWorkspace,
|
|
currentWorkspaceMember,
|
|
isSentryUserDefined,
|
|
]);
|
|
|
|
return <></>;
|
|
};
|