mirror of
https://github.com/hanzoai/sentinel.git
synced 2026-09-29 22:34:33 +00:00
FSL-1.0-Apache-2.0 grants us the code, not the name. "Sentry" is Functional Software's trademark, so every place the running app says it to a user -- page titles, onboarding, empty states, toasts, settings copy, the self-hosted footer -- now says Sentinel. Attribution is the other half of the deal and stays untouched: upstream copyright headers, LICENSE, NOTICE. So does anything where the mark is a fact rather than a brand -- @sentry/* packages, sentry-cli, the wizard and bundler plugins, docs.sentry.io, the whole gettingStartedDocs tree. Renaming those would leave instructions that no longer work. Nothing structural moved. No import, identifier, route path, feature flag, API field or test id changed; the diff is 312 lines in and 312 out. Colocated specs were updated in lockstep with the copy they assert. Marketing and community links point at hanzo.ai, only where a real page exists to point at. Co-authored-by: Hanzo Dev <[email protected]>
140 lines
4.3 KiB
TypeScript
140 lines
4.3 KiB
TypeScript
import {useEffect} from 'react';
|
|
import styled from '@emotion/styled';
|
|
import * as Sentry from '@sentry/react';
|
|
|
|
import {Alert} from 'sentry/components/alert';
|
|
import ExternalLink from 'sentry/components/links/externalLink';
|
|
import List from 'sentry/components/list';
|
|
import ListItem from 'sentry/components/list/listItem';
|
|
import {t, tct} from 'sentry/locale';
|
|
import OrganizationStore from 'sentry/stores/organizationStore';
|
|
import {useLegacyStore} from 'sentry/stores/useLegacyStore';
|
|
import {space} from 'sentry/styles/space';
|
|
import type {Project} from 'sentry/types';
|
|
import getRouteStringFromRoutes from 'sentry/utils/getRouteStringFromRoutes';
|
|
import {useRoutes} from 'sentry/utils/useRoutes';
|
|
import withProject from 'sentry/utils/withProject';
|
|
|
|
type Props = {
|
|
/**
|
|
* Disable logging to Sentry
|
|
*/
|
|
disableLogSentry?: boolean;
|
|
/**
|
|
* Disable the report dialog
|
|
*/
|
|
disableReport?: boolean;
|
|
error?: Error;
|
|
project?: Project;
|
|
};
|
|
|
|
function RouteError({error, disableLogSentry, disableReport, project}: Props) {
|
|
const routes = useRoutes();
|
|
const {organization} = useLegacyStore(OrganizationStore);
|
|
|
|
useEffect(() => {
|
|
if (disableLogSentry) {
|
|
return undefined;
|
|
}
|
|
if (!error) {
|
|
return undefined;
|
|
}
|
|
|
|
const route = getRouteStringFromRoutes(routes);
|
|
const enrichScopeContext = (scope: Sentry.Scope) => {
|
|
scope.setExtra('route', route);
|
|
scope.setExtra('orgFeatures', organization?.features ?? []);
|
|
scope.setExtra('orgAccess', organization?.access ?? []);
|
|
scope.setExtra('projectFeatures', project?.features ?? []);
|
|
return scope;
|
|
};
|
|
|
|
if (route) {
|
|
// Unexpectedly, error.message would sometimes not have a setter
|
|
// property, causing another exception to be thrown, and losing the
|
|
// original error in the process. Wrapping the mutation in a try-catch in
|
|
// an attempt to preserve the original error for logging.
|
|
//
|
|
// See https://github.com/getsentry/sentry/issues/16314 for more details.
|
|
try {
|
|
error.message = `${error.message}: ${route}`;
|
|
} catch (e) {
|
|
Sentry.withScope(scope => {
|
|
enrichScopeContext(scope);
|
|
scope.setExtra('cannotSetMessage', true);
|
|
});
|
|
}
|
|
}
|
|
|
|
// TODO(dcramer): show something in addition to embed (that contains it?)
|
|
// throw this in a timeout so if it errors we don't fall over
|
|
const reportDialogTimeout = window.setTimeout(() => {
|
|
Sentry.withScope(scope => {
|
|
enrichScopeContext(scope);
|
|
Sentry.captureException(error);
|
|
});
|
|
|
|
if (!disableReport) {
|
|
Sentry.showReportDialog();
|
|
}
|
|
});
|
|
|
|
return function cleanup() {
|
|
window.clearTimeout(reportDialogTimeout);
|
|
};
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [error, disableLogSentry]);
|
|
|
|
// Remove the report dialog on unmount
|
|
useEffect(() => () => document.querySelector('.sentry-error-embed-wrapper')?.remove());
|
|
|
|
// TODO(dcramer): show additional resource links
|
|
return (
|
|
<Alert type="error">
|
|
<Heading>{t('Oops! Something went wrong')}</Heading>
|
|
<p>
|
|
{t(`
|
|
It looks like you've hit an issue in our client application. Don't worry though!
|
|
We use Sentinel to monitor Sentinel and it's likely we're already looking into this!
|
|
`)}
|
|
</p>
|
|
<p>{t("If you're daring, you may want to try the following:")}</p>
|
|
<List symbol="bullet">
|
|
{window?.adblockSuspected && (
|
|
<ListItem>
|
|
{t(
|
|
"We detected something AdBlock-like. Try disabling it, as it's known to cause issues."
|
|
)}
|
|
</ListItem>
|
|
)}
|
|
<ListItem>
|
|
{tct(`Give it a few seconds and [link:reload the page].`, {
|
|
link: (
|
|
<a
|
|
onClick={() => {
|
|
window.location.href = window.location.href;
|
|
}}
|
|
/>
|
|
),
|
|
})}
|
|
</ListItem>
|
|
<ListItem>
|
|
{tct(`If all else fails, [link:contact us] with more details.`, {
|
|
link: (
|
|
<ExternalLink href="https://github.com/getsentry/sentry/issues/new/choose" />
|
|
),
|
|
})}
|
|
</ListItem>
|
|
</List>
|
|
</Alert>
|
|
);
|
|
}
|
|
|
|
const Heading = styled('h1')`
|
|
font-size: ${p => p.theme.fontSizeLarge};
|
|
line-height: 1.4;
|
|
margin-bottom: ${space(1)};
|
|
`;
|
|
|
|
export default withProject(RouteError);
|