Files
zeekayandHanzo Dev f888991a7a drop the Sentry wordmark from the react app
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]>
2026-08-10 18:01:22 -07:00

2569 lines
80 KiB
TypeScript

import {Fragment} from 'react';
import type {IndexRouteProps, RouteProps} from 'react-router';
import {
IndexRedirect,
IndexRoute as BaseIndexRoute,
Redirect,
Route as BaseRoute,
} from 'react-router';
import memoize from 'lodash/memoize';
import LazyLoad from 'sentry/components/lazyLoad';
import {EXPERIMENTAL_SPA, USING_CUSTOMER_DOMAIN} from 'sentry/constants';
import {t} from 'sentry/locale';
import HookStore from 'sentry/stores/hookStore';
import type {HookName} from 'sentry/types/hooks';
import errorHandler from 'sentry/utils/errorHandler';
import withDomainRedirect from 'sentry/utils/withDomainRedirect';
import withDomainRequired from 'sentry/utils/withDomainRequired';
import App from 'sentry/views/app';
import AuthLayout from 'sentry/views/auth/layout';
import {Tab, TabPaths} from 'sentry/views/issueDetails/types';
import IssueListContainer from 'sentry/views/issueList';
import IssueListOverview from 'sentry/views/issueList/overview';
import OrganizationContainer from 'sentry/views/organizationContainer';
import OrganizationLayout from 'sentry/views/organizationLayout';
import OrganizationRoot from 'sentry/views/organizationRoot';
import ProjectEventRedirect from 'sentry/views/projectEventRedirect';
import redirectDeprecatedProjectRoute from 'sentry/views/projects/redirectDeprecatedProjectRoute';
import RouteNotFound from 'sentry/views/routeNotFound';
import SettingsWrapper from 'sentry/views/settings/components/settingsWrapper';
type CustomProps = {
name?: string;
};
/**
* We add some additional props to our routes
*/
const Route = BaseRoute as React.ComponentClass<
React.PropsWithChildren<RouteProps & CustomProps>
>;
const IndexRoute = BaseIndexRoute as React.ComponentClass<IndexRouteProps & CustomProps>;
const hook = (name: HookName) => HookStore.get(name).map(cb => cb());
const SafeLazyLoad = errorHandler(LazyLoad);
// NOTE: makeLazyloadComponent is exported for use in the sentry.io (getsentry)
// pirvate routing tree.
/**
* Factory function to produce a component that will render the SafeLazyLoad
* _with_ the required props.
*/
export function makeLazyloadComponent<C extends React.ComponentType<any>>(
resolve: () => Promise<{default: C}>
) {
// XXX: Assign the component to a variable so it has a displayname
function RouteLazyLoad(props: React.ComponentProps<C>) {
// we can use this hook to set the organization as it's
// a child of the organization context
return <SafeLazyLoad {...props} component={resolve} />;
}
return RouteLazyLoad;
}
// Shorthand to avoid extra line wrapping
const make = makeLazyloadComponent;
function buildRoutes() {
// Read this to understand where to add new routes, how / why the routing
// tree is structured the way it is, and how the lazy-loading /
// code-splitting works for pages.
//
// ## Formatting
//
// NOTE that there are intentionally NO blank lines within route tree blocks.
// This helps make it easier to navigate within the file by using your
// editors shortcuts to jump between 'paragraphs' of code.
//
// [!!] Do NOT add blank lines within route blocks to preserve this behavior!
//
//
// ## Lazy loading
//
// * The `SafeLazyLoad` component
//
// Most routes are rendered as LazyLoad components (SafeLazyLoad is the
// errorHandler wrapped version). This means the rendered component for the
// route will only be loaded when the route is loaded. This helps us
// "code-split" the app.
//
// ## Hooks
//
// There are a number of `hook()` routes placed within the routing tree to
// allow for additional routes to be augmented into the application via the
// hookStore mechanism.
//
//
// ## The structure
//
// * `experimentalSpaRoutes`
//
// These routes are specifically for the experimental single-page-app mode,
// where Sentry is run separate from Django. These are NOT part of the root
// <App /> component.
//
// Right now these are mainly used for authentication pages. In the future
// they would be used for other pages like registration.
//
// * `rootRoutes`
//
// These routes live directly under the <App /> container, and generally
// are not specific to an organization.
//
// * `settingsRoutes`
//
// This is the route tree for all of `/settings/`. This route tree is
// composed of a few different sub-trees.
//
// - `accountSettingsRoutes` User specific settings
// - `orgSettingsRoutes` Specific to a organization
// - `projectSettingsRoutes` Specific to a project
// - `legacySettingsRedirects` Routes that used to exist in settings
//
// * `organizationRoutes`
//
// This is where a majority of the app routes live. This is wrapped with
// the <OrganizationLayout /> component, which renders the sidebar and
// loads the organiztion into context (though in some cases, there may be
// no organiztion)
//
// When adding new routes make sure you have both a route that starts
// with `/organizations/:orgId` and also 'customer-domains' route that
// does not include `/organizations/:orgId`. Often you'll only need to
// worry about this for the container route for that section of the UI.
// Child routes should access the current organization with `useOrganization()`
// or `withOrganization()` methods.
//
// Within these routes are a variety of subroutes. They are not all
// listed here as the subroutes will be added and removed, and most are
// self explanatory.
//
// * `legacyRedirectRoutes`
//
// This route tree contains <Redirect /> routes for many old legacy paths.
//
// You may also find <Redirect />'s collocated next to the feature routes
// they have redirects for. A good rule here is to place 'helper' redirects
// next to the routes they redirect to, and place 'legacy route' redirects
// for routes that have completely changed in this tree.
const experimentalSpaRoutes = EXPERIMENTAL_SPA ? (
<Route path="/auth/login/" component={errorHandler(AuthLayout)}>
<IndexRoute component={make(() => import('sentry/views/auth/login'))} />
<Route path=":orgId/" component={make(() => import('sentry/views/auth/login'))} />
</Route>
) : null;
const rootRoutes = (
<Fragment>
<IndexRoute component={make(() => import('sentry/views/app/root'))} />
{hook('routes:root')}
<Route
path="/accept/:orgId/:memberId/:token/"
component={make(() => import('sentry/views/acceptOrganizationInvite'))}
/>
<Route
path="/accept/:memberId/:token/"
component={make(() => import('sentry/views/acceptOrganizationInvite'))}
/>
<Route
path="/accept-transfer/"
component={make(() => import('sentry/views/acceptProjectTransfer'))}
/>
<Route component={errorHandler(OrganizationContainer)}>
<Route
path="/extensions/external-install/:integrationSlug/:installationId"
component={make(() => import('sentry/views/integrationOrganizationLink'))}
/>
<Route
path="/extensions/:integrationSlug/link/"
component={make(() => import('sentry/views/integrationOrganizationLink'))}
/>
</Route>
<Route
path="/sentry-apps/:sentryAppSlug/external-install/"
component={make(() => import('sentry/views/sentryAppExternalInstallation'))}
/>
<Redirect from="/account/" to="/settings/account/details/" />
<Redirect from="/share/group/:shareId/" to="/share/issue/:shareId/" />
{/* TODO: remove share/issue orgless url */}
<Route
path="/share/issue/:shareId/"
component={make(() => import('sentry/views/sharedGroupDetails'))}
/>
<Route
path="/organizations/:orgId/share/issue/:shareId/"
component={make(() => import('sentry/views/sharedGroupDetails'))}
/>
{USING_CUSTOMER_DOMAIN && (
<Route
path="/unsubscribe/project/:id/"
component={make(() => import('sentry/views/unsubscribe/project'))}
/>
)}
<Route
path="/unsubscribe/:orgId/project/:id/"
component={make(() => import('sentry/views/unsubscribe/project'))}
/>
{USING_CUSTOMER_DOMAIN && (
<Route
path="/unsubscribe/issue/:id/"
component={make(() => import('sentry/views/unsubscribe/issue'))}
/>
)}
<Route
path="/unsubscribe/:orgId/issue/:id/"
component={make(() => import('sentry/views/unsubscribe/issue'))}
/>
<Route
path="/organizations/new/"
component={make(() => import('sentry/views/organizationCreate'))}
/>
{USING_CUSTOMER_DOMAIN && (
<Route
path="/data-export/:dataExportId"
component={withDomainRequired(
make(() => import('sentry/views/dataExport/dataDownload'))
)}
key="orgless-data-export-route"
/>
)}
<Route
path="/organizations/:orgId/data-export/:dataExportId"
component={withDomainRedirect(
make(() => import('sentry/views/dataExport/dataDownload'))
)}
key="org-data-export"
/>
<Route component={errorHandler(OrganizationContainer)}>
{USING_CUSTOMER_DOMAIN && (
<Route
path="/disabled-member/"
component={withDomainRequired(
make(() => import('sentry/views/disabledMember'))
)}
key="orgless-disabled-member-route"
/>
)}
<Route
path="/organizations/:orgId/disabled-member/"
component={withDomainRedirect(
make(() => import('sentry/views/disabledMember'))
)}
key="org-disabled-member"
/>
</Route>
{USING_CUSTOMER_DOMAIN && (
<Route
path="/restore/"
component={make(() => import('sentry/views/organizationRestore'))}
/>
)}
<Route
path="/organizations/:orgId/restore/"
component={make(() => import('sentry/views/organizationRestore'))}
/>
{USING_CUSTOMER_DOMAIN && (
<Route
path="/join-request/"
component={withDomainRequired(
make(() => import('sentry/views/organizationJoinRequest'))
)}
key="orgless-join-request"
/>
)}
<Route
path="/join-request/:orgId/"
component={withDomainRedirect(
make(() => import('sentry/views/organizationJoinRequest'))
)}
key="org-join-request"
/>
<Route
path="/relocation/"
component={make(() => import('sentry/views/relocation'))}
key="orgless-relocation"
>
<IndexRedirect to="get-started/" />
<Route path=":step/" component={make(() => import('sentry/views/relocation'))} />
</Route>
{USING_CUSTOMER_DOMAIN && (
<Route
path="/onboarding/"
component={errorHandler(withDomainRequired(OrganizationContainer))}
key="orgless-onboarding"
>
<IndexRedirect to="welcome/" />
<Route
path=":step/"
component={make(() => import('sentry/views/onboarding'))}
/>
</Route>
)}
<Route
path="/onboarding/:orgId/"
component={withDomainRedirect(errorHandler(OrganizationContainer))}
key="org-onboarding"
>
<IndexRedirect to="welcome/" />
<Route path=":step/" component={make(() => import('sentry/views/onboarding'))} />
</Route>
{USING_CUSTOMER_DOMAIN && (
<Route
path="/stories/"
component={make(() => import('sentry/views/stories/index'))}
key="orgless-stories"
/>
)}
<Route
path="/organizations/:orgId/stories/"
component={withDomainRedirect(make(() => import('sentry/views/stories/index')))}
key="org-stories"
/>
</Fragment>
);
const accountSettingsRoutes = (
<Route
path="account/"
name={t('Account')}
component={make(
() => import('sentry/views/settings/account/accountSettingsLayout')
)}
>
<IndexRedirect to="details/" />
<Route
path="details/"
name={t('Details')}
component={make(() => import('sentry/views/settings/account/accountDetails'))}
/>
<Route path="notifications/" name={t('Notifications')}>
<IndexRoute
component={make(
() =>
import(
'sentry/views/settings/account/notifications/notificationSettingsController'
)
)}
/>
<Route
path=":fineTuneType/"
name={t('Fine Tune Alerts')}
component={make(
() =>
import(
'sentry/views/settings/account/accountNotificationFineTuningController'
)
)}
/>
</Route>
<Route
path="emails/"
name={t('Emails')}
component={make(() => import('sentry/views/settings/account/accountEmails'))}
/>
<Route
path="authorizations/"
component={make(
() => import('sentry/views/settings/account/accountAuthorizations')
)}
/>
<Route path="security/" name={t('Security')}>
<Route
component={make(
() =>
import(
'sentry/views/settings/account/accountSecurity/accountSecurityWrapper'
)
)}
>
<IndexRoute
component={make(
() => import('sentry/views/settings/account/accountSecurity')
)}
/>
<Route
path="session-history/"
name={t('Session History')}
component={make(
() => import('sentry/views/settings/account/accountSecurity/sessionHistory')
)}
/>
<Route
path="mfa/:authId/"
name={t('Details')}
component={make(
() =>
import(
'sentry/views/settings/account/accountSecurity/accountSecurityDetails'
)
)}
/>
</Route>
<Route
path="mfa/:authId/enroll/"
name={t('Enroll')}
component={make(
() =>
import(
'sentry/views/settings/account/accountSecurity/accountSecurityEnroll'
)
)}
/>
</Route>
<Route
path="subscriptions/"
name={t('Subscriptions')}
component={make(
() => import('sentry/views/settings/account/accountSubscriptions')
)}
/>
<Route
path="identities/"
name={t('Identities')}
component={make(() => import('sentry/views/settings/account/accountIdentities'))}
/>
<Route path="api/" name={t('API')}>
<IndexRedirect to="auth-tokens/" />
<Route path="auth-tokens/" name={t('User Auth Tokens')}>
<IndexRoute
component={make(() => import('sentry/views/settings/account/apiTokens'))}
/>
<Route
path="new-token/"
name={t('Create New Token')}
component={make(() => import('sentry/views/settings/account/apiNewToken'))}
/>
</Route>
<Route path="applications/" name={t('Applications')}>
<IndexRoute
component={make(
() => import('sentry/views/settings/account/apiApplications')
)}
/>
<Route
path=":appId/"
name={t('Details')}
component={make(
() => import('sentry/views/settings/account/apiApplications/details')
)}
/>
</Route>
{hook('routes:api')}
</Route>
<Route
path="close-account/"
name={t('Close Account')}
component={make(() => import('sentry/views/settings/account/accountClose'))}
/>
</Route>
);
const projectSettingsRoutes = (
<Route
path="projects/:projectId/"
name={t('Project')}
component={make(
() => import('sentry/views/settings/project/projectSettingsLayout')
)}
>
<IndexRoute
name={t('General')}
component={make(() => import('sentry/views/settings/projectGeneralSettings'))}
/>
<Redirect from="install/" to="/projects/:projectId/getting-started/" />
<Route
path="teams/"
name={t('Teams')}
component={make(() => import('sentry/views/settings/project/projectTeams'))}
/>
<Route
path="alerts/"
name={t('Alerts')}
component={make(() => import('sentry/views/settings/projectAlerts'))}
>
<IndexRoute
component={make(() => import('sentry/views/settings/projectAlerts/settings'))}
/>
<Redirect from="new/" to="/organizations/:orgId/alerts/:projectId/new/" />
<Redirect from="rules/" to="/organizations/:orgId/alerts/rules/" />
<Redirect from="rules/new/" to="/organizations/:orgId/alerts/:projectId/new/" />
<Redirect
from="metric-rules/new/"
to="/organizations/:orgId/alerts/:projectId/new/"
/>
<Redirect
from="rules/:ruleId/"
to="/organizations/:orgId/alerts/rules/:projectId/:ruleId/"
/>
<Redirect
from="metric-rules/:ruleId/"
to="/organizations/:orgId/alerts/metric-rules/:projectId/:ruleId/"
/>
</Route>
<Route
path="environments/"
name={t('Environments')}
component={make(
() => import('sentry/views/settings/project/projectEnvironments')
)}
>
<IndexRoute />
<Route path="hidden/" />
</Route>
<Route
path="tags/"
name={t('Tags')}
component={make(() => import('sentry/views/settings/projectTags'))}
/>
<Redirect from="issue-tracking/" to="/settings/:orgId/:projectId/plugins/" />
<Route
path="release-tracking/"
name={t('Release Tracking')}
component={make(
() => import('sentry/views/settings/project/projectReleaseTracking')
)}
/>
<Route
path="ownership/"
name={t('Ownership Rules')}
component={make(() => import('sentry/views/settings/project/projectOwnership'))}
/>
<Route
path="data-forwarding/"
name={t('Data Forwarding')}
component={make(() => import('sentry/views/settings/projectDataForwarding'))}
/>
<Route path="security-and-privacy/" name={t('Security & Privacy')}>
<IndexRoute
component={make(
() => import('sentry/views/settings/projectSecurityAndPrivacy')
)}
/>
<Route
path="advanced-data-scrubbing/:scrubbingId/"
component={make(
() => import('sentry/views/settings/projectSecurityAndPrivacy')
)}
/>
</Route>
<Route
path="debug-symbols/"
name={t('Debug Information Files')}
component={make(() => import('sentry/views/settings/projectDebugFiles'))}
/>
<Route
path="proguard/"
name={t('ProGuard Mappings')}
component={make(() => import('sentry/views/settings/projectProguard'))}
/>
<Route
path="performance/"
name={t('Performance')}
component={make(() => import('sentry/views/settings/projectPerformance'))}
/>
<Route path="metrics/" name={t('Metrics')}>
<IndexRoute
component={make(() => import('sentry/views/settings/projectMetrics'))}
/>
<Route
name={t('Metrics Details')}
path=":mri/"
component={make(
() => import('sentry/views/settings/projectMetrics/projectMetricsDetails')
)}
/>
</Route>
<Route
path="replays/"
name={t('Replays')}
component={make(() => import('sentry/views/settings/project/projectReplays'))}
/>
<Route path="source-maps/" name={t('Source Maps')}>
<IndexRoute
component={make(() => import('sentry/views/settings/projectSourceMaps'))}
/>
<Route
path="artifact-bundles/"
name={t('Artifact Bundles')}
component={make(() => import('sentry/views/settings/projectSourceMaps'))}
>
<Route
name={t('Artifact Bundle')}
path=":bundleId/"
component={make(() => import('sentry/views/settings/projectSourceMaps'))}
/>
</Route>
<Route
path="release-bundles/"
name={t('Release Bundles')}
component={make(() => import('sentry/views/settings/projectSourceMaps'))}
>
<Route
name={t('Release Bundle')}
path=":bundleId/"
component={make(() => import('sentry/views/settings/projectSourceMaps'))}
/>
</Route>
<Redirect from=":name/" to="release-bundles/:name/" />
</Route>
<Route
path="processing-issues/"
name={t('Processing Issues')}
component={make(
() => import('sentry/views/settings/project/projectProcessingIssues')
)}
/>
<Route
path="filters/"
name={t('Inbound Filters')}
component={make(() => import('sentry/views/settings/project/projectFilters'))}
>
<IndexRedirect to="data-filters/" />
<Route path=":filterType/" />
</Route>
<Redirect from="dynamic-sampling/" to="performance/" />
<Route
path="issue-grouping/"
name={t('Issue Grouping')}
component={make(() => import('sentry/views/settings/projectIssueGrouping'))}
/>
<Route
path="hooks/"
name={t('Service Hooks')}
component={make(
() => import('sentry/views/settings/project/projectServiceHooks')
)}
/>
<Route
path="hooks/new/"
name={t('Create Service Hook')}
component={make(
() => import('sentry/views/settings/project/projectCreateServiceHook')
)}
/>
<Route
path="hooks/:hookId/"
name={t('Service Hook Details')}
component={make(
() => import('sentry/views/settings/project/projectServiceHookDetails')
)}
/>
<Route path="keys/" name={t('Client Keys')}>
<IndexRoute
component={make(() => import('sentry/views/settings/project/projectKeys/list'))}
/>
<Route
path=":keyId/"
name={t('Details')}
component={make(
() => import('sentry/views/settings/project/projectKeys/details')
)}
/>
</Route>
<Route
path="loader-script/"
name={t('Loader Script')}
component={make(() => import('sentry/views/settings/project/loaderScript'))}
/>
<Route
path="user-feedback/"
name={t('User Feedback')}
component={make(
() => import('sentry/views/settings/project/projectUserFeedback')
)}
/>
<Redirect from="csp/" to="security-headers/" />
<Route path="security-headers/" name={t('Security Headers')}>
<IndexRoute
component={make(() => import('sentry/views/settings/projectSecurityHeaders'))}
/>
<Route
path="csp/"
name={t('Content Security Policy')}
component={make(
() => import('sentry/views/settings/projectSecurityHeaders/csp')
)}
/>
<Route
path="expect-ct/"
name={t('Certificate Transparency')}
component={make(
() => import('sentry/views/settings/projectSecurityHeaders/expectCt')
)}
/>
<Route
path="hpkp/"
name={t('HPKP')}
component={make(
() => import('sentry/views/settings/projectSecurityHeaders/hpkp')
)}
/>
</Route>
<Route path="plugins/" name={t('Legacy Integrations')}>
<IndexRoute
component={make(() => import('sentry/views/settings/projectPlugins'))}
/>
<Route
path=":pluginId/"
name={t('Integration Details')}
component={make(() => import('sentry/views/settings/projectPlugins/details'))}
/>
</Route>
</Route>
);
const orgSettingsRoutes = (
<Route
component={make(
() => import('sentry/views/settings/organization/organizationSettingsLayout')
)}
>
{hook('routes:organization')}
{!USING_CUSTOMER_DOMAIN && (
<IndexRoute
name={t('General')}
component={make(
() => import('sentry/views/settings/organizationGeneralSettings')
)}
/>
)}
{USING_CUSTOMER_DOMAIN && (
<Route
path="/settings/organization/"
name={t('General')}
component={make(
() => import('sentry/views/settings/organizationGeneralSettings')
)}
/>
)}
<Route
path="projects/"
name={t('Projects')}
component={make(() => import('sentry/views/settings/organizationProjects'))}
/>
<Route path="api-keys/" name={t('API Key')}>
<IndexRoute
component={make(() => import('sentry/views/settings/organizationApiKeys'))}
/>
<Route
path=":apiKey/"
name={t('Details')}
component={make(
() =>
import(
'sentry/views/settings/organizationApiKeys/organizationApiKeyDetails'
)
)}
/>
</Route>
<Route
path="audit-log/"
name={t('Audit Log')}
component={make(() => import('sentry/views/settings/organizationAuditLog'))}
/>
<Route
path="auth/"
name={t('Auth Providers')}
component={make(() => import('sentry/views/settings/organizationAuth'))}
/>
<Redirect from="members/requests" to="members/" />
<Route path="members/" name={t('Members')}>
<Route
component={make(
() =>
import(
'sentry/views/settings/organizationMembers/organizationMembersWrapper'
)
)}
>
<IndexRoute
component={make(
() =>
import(
'sentry/views/settings/organizationMembers/organizationMembersList'
)
)}
/>
</Route>
<Route
path=":memberId/"
name={t('Details')}
component={make(
() =>
import('sentry/views/settings/organizationMembers/organizationMemberDetail')
)}
/>
</Route>
<Route
path="rate-limits/"
name={t('Rate Limits')}
component={make(() => import('sentry/views/settings/organizationRateLimits'))}
/>
<Route
path="relay/"
name={t('Relay')}
component={make(() => import('sentry/views/settings/organizationRelay'))}
/>
<Route
path="repos/"
name={t('Repositories')}
component={make(() => import('sentry/views/settings/organizationRepositories'))}
/>
<Route
path="settings/"
component={make(
() => import('sentry/views/settings/organizationGeneralSettings')
)}
/>
<Route path="security-and-privacy/" name={t('Security & Privacy')}>
<IndexRoute
component={make(
() => import('sentry/views/settings/organizationSecurityAndPrivacy')
)}
/>
<Route
path="advanced-data-scrubbing/:scrubbingId/"
component={make(
() => import('sentry/views/settings/organizationSecurityAndPrivacy')
)}
/>
</Route>
<Route path="teams/" name={t('Teams')}>
<IndexRoute
component={make(() => import('sentry/views/settings/organizationTeams'))}
/>
<Route
path=":teamId/"
name={t('Team')}
component={make(
() => import('sentry/views/settings/organizationTeams/teamDetails')
)}
>
<IndexRedirect to="members/" />
<Route
path="members/"
name={t('Members')}
component={make(
() => import('sentry/views/settings/organizationTeams/teamMembers')
)}
/>
<Route
path="notifications/"
name={t('Notifications')}
component={make(
() => import('sentry/views/settings/organizationTeams/teamNotifications')
)}
/>
<Route
path="projects/"
name={t('Projects')}
component={make(
() => import('sentry/views/settings/organizationTeams/teamProjects')
)}
/>
<Route
path="settings/"
name={t('Settings')}
component={make(
() => import('sentry/views/settings/organizationTeams/teamSettings')
)}
/>
</Route>
</Route>
<Redirect from="plugins/" to="integrations/" />
<Route path="plugins/" name={t('Integrations')}>
<Route
path=":integrationSlug/"
name={t('Integration Details')}
component={make(
() =>
import('sentry/views/settings/organizationIntegrations/pluginDetailedView')
)}
/>
</Route>
<Redirect from="sentry-apps/" to="integrations/" />
<Route path="sentry-apps/" name={t('Integrations')}>
<Route
path=":integrationSlug"
name={t('Details')}
component={make(
() =>
import(
'sentry/views/settings/organizationIntegrations/sentryAppDetailedView'
)
)}
/>
</Route>
<Redirect from="document-integrations/" to="integrations/" />
<Route path="document-integrations/" name={t('Integrations')}>
<Route
path=":integrationSlug"
name={t('Details')}
component={make(
() =>
import(
'sentry/views/settings/organizationIntegrations/docIntegrationDetailedView'
)
)}
/>
</Route>
<Route path="integrations/" name={t('Integrations')}>
<IndexRoute
component={make(
() =>
import(
'sentry/views/settings/organizationIntegrations/integrationListDirectory'
)
)}
/>
<Route
path=":integrationSlug"
name={t('Integration Details')}
component={make(
() =>
import(
'sentry/views/settings/organizationIntegrations/integrationDetailedView'
)
)}
/>
<Route
path=":providerKey/:integrationId/"
name={t('Configure Integration')}
component={make(
() =>
import(
'sentry/views/settings/organizationIntegrations/configureIntegration'
)
)}
/>
</Route>
<Redirect from="developer-settings/sentry-functions/" to="developer-settings/" />
<Route path="developer-settings/" name={t('Custom Integrations')}>
<IndexRoute
component={make(
() => import('sentry/views/settings/organizationDeveloperSettings')
)}
/>
<Route
path="new-public/"
name={t('Create Integration')}
component={make(
() =>
import(
'sentry/views/settings/organizationDeveloperSettings/sentryApplicationDetails'
)
)}
/>
<Route
path="new-internal/"
name={t('Create Integration')}
component={make(
() =>
import(
'sentry/views/settings/organizationDeveloperSettings/sentryApplicationDetails'
)
)}
/>
<Route
path=":appSlug/"
name={t('Edit Integration')}
component={make(
() =>
import(
'sentry/views/settings/organizationDeveloperSettings/sentryApplicationDetails'
)
)}
/>
<Route
path=":appSlug/dashboard/"
name={t('Integration Dashboard')}
component={make(
() =>
import(
'sentry/views/settings/organizationDeveloperSettings/sentryApplicationDashboard'
)
)}
/>
<Route path="sentry-functions/" name={t('Sentinel Functions')}>
<Route
path="new/"
name={t('Create Sentinel Function')}
component={make(
() =>
import(
'sentry/views/settings/organizationDeveloperSettings/sentryFunctionDetails'
)
)}
/>
<Route
path=":functionSlug/"
name={t('Edit Sentinel Function')}
component={make(
() =>
import(
'sentry/views/settings/organizationDeveloperSettings/sentryFunctionDetails'
)
)}
/>
</Route>
</Route>
<Route path="auth-tokens/" name={t('Auth Tokens')}>
<IndexRoute
component={make(() => import('sentry/views/settings/organizationAuthTokens'))}
/>
<Route
path="new-token/"
name={t('Create New Auth Token')}
component={make(
() => import('sentry/views/settings/organizationAuthTokens/newAuthToken')
)}
/>
<Route
path=":tokenId/"
name={t('Edit Auth Token')}
component={make(
() => import('sentry/views/settings/organizationAuthTokens/authTokenDetails')
)}
/>
</Route>
<Route
path="early-features/"
name={t('Early Features')}
component={make(() => import('sentry/views/settings/earlyFeatures'))}
/>
</Route>
);
const legacySettingsRedirects = (
<Fragment>
<Redirect from=":projectId/" to="projects/:projectId/" />
<Redirect from=":projectId/alerts/" to="projects/:projectId/alerts/" />
<Redirect from=":projectId/alerts/rules/" to="projects/:projectId/alerts/rules/" />
<Redirect
from=":projectId/alerts/rules/:ruleId/"
to="projects/:projectId/alerts/rules/:ruleId/"
/>
</Fragment>
);
const settingsRoutes = (
<Route path="/settings/" name={t('Settings')} component={SettingsWrapper}>
<IndexRoute component={make(() => import('sentry/views/settings/settingsIndex'))} />
{accountSettingsRoutes}
<Fragment>
{USING_CUSTOMER_DOMAIN && (
<Route
name={t('Organization')}
component={withDomainRequired(NoOp)}
key="orgless-settings-route"
>
{orgSettingsRoutes}
{projectSettingsRoutes}
</Route>
)}
<Route
path=":orgId/"
name={t('Organization')}
component={withDomainRedirect(NoOp)}
key="org-settings"
>
{orgSettingsRoutes}
{projectSettingsRoutes}
{legacySettingsRedirects}
</Route>
</Fragment>
</Route>
);
const projectsChildRoutes = (
<Fragment>
<IndexRoute component={make(() => import('sentry/views/projectsDashboard'))} />
<Route
path="new/"
component={make(() => import('sentry/views/projectInstall/newProject'))}
/>
<Route
path=":projectId/"
component={make(() => import('sentry/views/projectDetail'))}
/>
<Route
path=":projectId/events/:eventId/"
component={errorHandler(ProjectEventRedirect)}
/>
<Route
path=":projectId/getting-started/"
component={make(
() => import('sentry/views/projectInstall/platformOrIntegration')
)}
/>
</Fragment>
);
const projectsRoutes = (
<Fragment>
{USING_CUSTOMER_DOMAIN && (
<Route
path="/projects/"
component={make(() => import('sentry/views/projects/'))}
key="orgless-projects-route"
>
{projectsChildRoutes}
</Route>
)}
<Route
path="/organizations/:orgId/projects/"
component={make(() => import('sentry/views/projects/'))}
key="org-projects"
>
{projectsChildRoutes}
</Route>
</Fragment>
);
const dashboardWidgetRoutes = (
<Fragment>
<Route
path="widget/:widgetIndex/edit/"
component={make(() => import('sentry/views/dashboards/widgetBuilder'))}
/>
<Route
path="widget/new/"
component={make(() => import('sentry/views/dashboards/widgetBuilder'))}
/>
<Route
path="widget/:widgetId/"
component={make(() => import('sentry/views/dashboards/view'))}
/>
</Fragment>
);
const dashboardRoutes = (
<Fragment>
<Fragment>
{USING_CUSTOMER_DOMAIN && (
<Route
path="/dashboards/"
component={withDomainRequired(make(() => import('sentry/views/dashboards')))}
key="orgless-dashboards-route"
>
<IndexRoute
component={make(() => import('sentry/views/dashboards/manage'))}
/>
</Route>
)}
<Route
path="/organizations/:orgId/dashboards/"
component={withDomainRedirect(make(() => import('sentry/views/dashboards')))}
key="org-dashboards"
>
<IndexRoute component={make(() => import('sentry/views/dashboards/manage'))} />
</Route>
</Fragment>
<Fragment>
{USING_CUSTOMER_DOMAIN && (
<Route
path="/dashboards/new/"
component={withDomainRequired(
make(() => import('sentry/views/dashboards/create'))
)}
key="orgless-dashboards-new-route"
>
<Route
path="widget/:widgetIndex/edit/"
component={make(() => import('sentry/views/dashboards/widgetBuilder'))}
/>
<Route
path="widget/new/"
component={make(() => import('sentry/views/dashboards/widgetBuilder'))}
/>
</Route>
)}
<Route
path="/organizations/:orgId/dashboards/new/"
component={withDomainRedirect(
make(() => import('sentry/views/dashboards/create'))
)}
key="org-dashboards-new"
>
<Route
path="widget/:widgetIndex/edit/"
component={make(() => import('sentry/views/dashboards/widgetBuilder'))}
/>
<Route
path="widget/new/"
component={make(() => import('sentry/views/dashboards/widgetBuilder'))}
/>
</Route>
</Fragment>
<Fragment>
{USING_CUSTOMER_DOMAIN && (
<Route
path="/dashboards/new/:templateId"
component={withDomainRequired(
make(() => import('sentry/views/dashboards/create'))
)}
key="orgless-dashboards-new-template-route"
>
<Route
path="widget/:widgetId/"
component={make(() => import('sentry/views/dashboards/create'))}
/>
</Route>
)}
<Route
path="/organizations/:orgId/dashboards/new/:templateId"
component={withDomainRedirect(
make(() => import('sentry/views/dashboards/create'))
)}
key="org-dashboards-new-template"
>
<Route
path="widget/:widgetId/"
component={make(() => import('sentry/views/dashboards/create'))}
/>
</Route>
</Fragment>
<Redirect
from="/organizations/:orgId/dashboards/:dashboardId/"
to="/organizations/:orgId/dashboard/:dashboardId/"
/>
{USING_CUSTOMER_DOMAIN && (
<Redirect from="/dashboards/:dashboardId/" to="/dashboard/:dashboardId/" />
)}
<Fragment>
{USING_CUSTOMER_DOMAIN && (
<Route
path="/dashboard/:dashboardId/"
component={withDomainRequired(
make(() => import('sentry/views/dashboards/view'))
)}
key="orgless-dashboards-dashboard-id-route"
>
{dashboardWidgetRoutes}
</Route>
)}
<Route
path="/organizations/:orgId/dashboard/:dashboardId/"
component={withDomainRedirect(
make(() => import('sentry/views/dashboards/view'))
)}
key="org-dashboards-dashboard-id"
>
{dashboardWidgetRoutes}
</Route>
</Fragment>
</Fragment>
);
const alertChildRoutes = ({forCustomerDomain}: {forCustomerDomain: boolean}) => {
return (
<Fragment>
<IndexRoute
component={make(() => import('sentry/views/alerts/list/incidents'))}
/>
<Route path="rules/">
<IndexRoute
component={make(
() => import('sentry/views/alerts/list/rules/alertRulesList')
)}
/>
<Route
path="details/:ruleId/"
component={make(() => import('sentry/views/alerts/rules/metric/details'))}
/>
<Route
path=":projectId/"
component={make(() => import('sentry/views/alerts/builder/projectProvider'))}
>
<IndexRedirect
to={
forCustomerDomain
? '/alerts/rules/'
: '/organizations/:orgId/alerts/rules/'
}
/>
<Route
path=":ruleId/"
component={make(() => import('sentry/views/alerts/edit'))}
/>
</Route>
<Route path=":projectId/:ruleId/details/">
<IndexRoute
component={make(
() => import('sentry/views/alerts/rules/issue/details/ruleDetails')
)}
/>
</Route>
</Route>
<Route path="metric-rules/">
<IndexRedirect
to={
forCustomerDomain ? '/alerts/rules/' : '/organizations/:orgId/alerts/rules/'
}
/>
<Route
path=":projectId/"
component={make(() => import('sentry/views/alerts/builder/projectProvider'))}
>
<IndexRedirect
to={
forCustomerDomain
? '/alerts/rules/'
: '/organizations/:orgId/alerts/rules/'
}
/>
<Route
path=":ruleId/"
component={make(() => import('sentry/views/alerts/edit'))}
/>
</Route>
</Route>
<Route
path="wizard/"
component={make(() => import('sentry/views/alerts/builder/projectProvider'))}
>
<IndexRoute component={make(() => import('sentry/views/alerts/wizard'))} />
</Route>
<Route
path="new/"
component={make(() => import('sentry/views/alerts/builder/projectProvider'))}
>
<IndexRedirect
to={
forCustomerDomain
? '/alerts/wizard/'
: '/organizations/:orgId/alerts/wizard/'
}
/>
<Route
path=":alertType/"
component={make(() => import('sentry/views/alerts/create'))}
/>
</Route>
<Route
path=":alertId/"
component={make(() => import('sentry/views/alerts/incidentRedirect'))}
/>
<Route
path=":projectId/"
component={make(() => import('sentry/views/alerts/builder/projectProvider'))}
>
<Route
path="new/"
component={make(() => import('sentry/views/alerts/create'))}
/>
<Route
path="wizard/"
component={make(() => import('sentry/views/alerts/wizard'))}
/>
</Route>
</Fragment>
);
};
const alertRoutes = (
<Fragment>
{USING_CUSTOMER_DOMAIN && (
<Route
path="/alerts/"
component={withDomainRequired(make(() => import('sentry/views/alerts')))}
key="orgless-alerts-route"
>
{alertChildRoutes({forCustomerDomain: true})}
</Route>
)}
<Route
path="/organizations/:orgId/alerts/"
component={withDomainRedirect(make(() => import('sentry/views/alerts')))}
key="org-alerts"
>
{alertChildRoutes({forCustomerDomain: false})}
</Route>
</Fragment>
);
const cronsChildRoutes = (
<Fragment>
<IndexRoute component={make(() => import('sentry/views/monitors/overview'))} />
<Route
path="create/"
component={make(() => import('sentry/views/monitors/create'))}
/>
<Route
path=":monitorSlug/"
component={make(() => import('sentry/views/monitors/details'))}
/>
<Route
path=":monitorSlug/edit/"
component={make(() => import('sentry/views/monitors/edit'))}
/>
</Fragment>
);
const cronsRoutes = (
<Fragment>
{USING_CUSTOMER_DOMAIN && (
<Route
path="/crons/"
component={withDomainRequired(make(() => import('sentry/views/monitors')))}
key="orgless-monitors-route"
>
{cronsChildRoutes}
</Route>
)}
<Route
path="/organizations/:orgId/crons/"
component={withDomainRedirect(make(() => import('sentry/views/monitors')))}
key="org-monitors"
>
{cronsChildRoutes}
</Route>
</Fragment>
);
const replayChildRoutes = (
<Fragment>
<IndexRoute component={make(() => import('sentry/views/replays/list'))} />
<Route
path="selectors/"
component={make(
() => import('sentry/views/replays/deadRageClick/deadRageClickList')
)}
/>
<Route
path=":replaySlug/"
component={make(() => import('sentry/views/replays/details'))}
/>
</Fragment>
);
const replayRoutes = (
<Fragment>
{USING_CUSTOMER_DOMAIN && (
<Route
path="/replays/"
component={withDomainRequired(make(() => import('sentry/views/replays/index')))}
key="orgless-replays-route"
>
{replayChildRoutes}
</Route>
)}
<Route
path="/organizations/:orgId/replays/"
component={withDomainRedirect(make(() => import('sentry/views/replays/index')))}
key="org-replays"
>
{replayChildRoutes}
</Route>
</Fragment>
);
const releasesChildRoutes = ({forCustomerDomain}: {forCustomerDomain: boolean}) => {
return (
<Fragment>
<IndexRoute component={make(() => import('sentry/views/releases/list'))} />
<Route
path=":release/"
component={make(() => import('sentry/views/releases/detail'))}
>
<IndexRoute
component={make(() => import('sentry/views/releases/detail/overview'))}
/>
<Route
path="commits/"
component={make(
() => import('sentry/views/releases/detail/commitsAndFiles/commits')
)}
/>
<Route
path="files-changed/"
component={make(
() => import('sentry/views/releases/detail/commitsAndFiles/filesChanged')
)}
/>
{forCustomerDomain ? null : (
<Fragment>
<Redirect
from="new-events/"
to="/organizations/:orgId/releases/:release/"
/>
<Redirect
from="all-events/"
to="/organizations/:orgId/releases/:release/"
/>
</Fragment>
)}
</Route>
</Fragment>
);
};
const releasesRoutes = (
<Fragment>
{USING_CUSTOMER_DOMAIN && (
<Route
path="/releases/"
component={withDomainRequired(NoOp)}
key="orgless-releases-route"
>
{releasesChildRoutes({forCustomerDomain: true})}
</Route>
)}
<Route
path="/organizations/:orgId/releases/"
component={withDomainRedirect(NoOp)}
key="org-releases"
>
{releasesChildRoutes({forCustomerDomain: false})}
</Route>
</Fragment>
);
const releaseThresholdRoutes = (
<Fragment>
{USING_CUSTOMER_DOMAIN && (
<Route
path="/release-thresholds/"
component={withDomainRequired(NoOp)}
key="orgless-release-thresholds-route"
>
<IndexRoute
component={make(() => import('sentry/views/releases/thresholdsList'))}
/>
</Route>
)}
<Route
path="/organizations/:orgId/release-thresholds/"
component={withDomainRedirect(NoOp)}
key="org-release-thresholds"
>
<IndexRoute
component={make(() => import('sentry/views/releases/thresholdsList'))}
/>
</Route>
</Fragment>
);
const activityRoutes = (
<Fragment>
{USING_CUSTOMER_DOMAIN && (
<Route
path="/activity/"
component={withDomainRequired(
make(() => import('sentry/views/organizationActivity'))
)}
key="orgless-activity-route"
/>
)}
<Route
path="/organizations/:orgId/activity/"
component={withDomainRedirect(
make(() => import('sentry/views/organizationActivity'))
)}
key="org-activity"
/>
</Fragment>
);
const statsChildRoutes = ({forCustomerDomain}: {forCustomerDomain: boolean}) => {
return (
<Fragment>
<IndexRoute component={make(() => import('sentry/views/organizationStats'))} />
<Route
path="issues/"
component={make(() => import('sentry/views/organizationStats/teamInsights'))}
>
<IndexRoute
component={make(
() => import('sentry/views/organizationStats/teamInsights/issues')
)}
/>
</Route>
<Route
path="health/"
component={make(() => import('sentry/views/organizationStats/teamInsights'))}
>
<IndexRoute
component={make(
() => import('sentry/views/organizationStats/teamInsights/health')
)}
/>
</Route>
{forCustomerDomain ? null : (
<Redirect from="team/" to="/organizations/:orgId/stats/issues/" />
)}
</Fragment>
);
};
const statsRoutes = (
<Fragment>
{USING_CUSTOMER_DOMAIN && (
<Route
path="/stats/"
component={withDomainRequired(NoOp)}
key="orgless-stats-route"
>
{statsChildRoutes({forCustomerDomain: true})}
</Route>
)}
<Route
path="/organizations/:orgId/stats/"
component={withDomainRedirect(NoOp)}
key="org-stats"
>
{statsChildRoutes({forCustomerDomain: false})}
</Route>
</Fragment>
);
// TODO(mark) Long term this /queries route should go away and /discover
// should be the canonical route for discover2. We have a redirect right now
// as /discover was for discover 1 and most of the application is linking to
// /discover/queries and not /discover
const discoverChildRoutes = (
<Fragment>
<IndexRedirect to="queries/" />
<Route
path="homepage/"
component={make(() => import('sentry/views/discover/homepage'))}
/>
<Route
path="queries/"
component={make(() => import('sentry/views/discover/landing'))}
/>
<Route
path="results/"
component={make(() => import('sentry/views/discover/results'))}
/>
<Route
path=":eventSlug/"
component={make(() => import('sentry/views/discover/eventDetails'))}
/>
</Fragment>
);
const discoverRoutes = (
<Fragment>
{USING_CUSTOMER_DOMAIN && (
<Route
path="/discover/"
component={withDomainRequired(make(() => import('sentry/views/discover')))}
key="orgless-discover-route"
>
{discoverChildRoutes}
</Route>
)}
<Route
path="/organizations/:orgId/discover/"
component={withDomainRedirect(make(() => import('sentry/views/discover')))}
key="org-discover-route"
>
{discoverChildRoutes}
</Route>
</Fragment>
);
const performanceChildRoutes = (
<Fragment>
<IndexRoute component={make(() => import('sentry/views/performance/content'))} />
<Route
path="trends/"
component={make(() => import('sentry/views/performance/trends'))}
/>
<Route path="database/">
<IndexRoute
component={make(
() => import('sentry/views/performance/database/databaseLandingPage')
)}
/>
<Route
path="spans/span/:groupId/"
component={make(
() => import('sentry/views/performance/database/databaseSpanSummaryPage')
)}
/>
</Route>
<Route path="browser/">
<Route path="interactions/">
<IndexRoute
component={make(
() => import('sentry/views/performance/browser/interactionsLandingPage')
)}
/>
<Route
path="summary/"
component={make(
() => import('sentry/views/performance/browser/interactionSummary/index')
)}
/>
</Route>
<Route path="pageloads/">
<IndexRoute
component={make(
() =>
import('sentry/views/performance/browser/webVitals/webVitalsLandingPage')
)}
/>
<Route
path="overview/"
component={make(
() => import('sentry/views/performance/browser/webVitals/pageOverview')
)}
/>
</Route>
<Route path="resources/">
<IndexRoute
component={make(
() => import('sentry/views/performance/browser/resources/index')
)}
/>
<Route
path="spans/span/:groupId/"
component={make(
() =>
import(
'sentry/views/performance/browser/resources/resourceSummaryPage/index'
)
)}
/>
</Route>
</Route>
<Route path="mobile/">
<Route path="screens/">
<IndexRoute
component={make(
() => import('sentry/views/starfish/modules/mobile/pageload')
)}
/>
<Route
path="spans/"
component={make(
() => import('sentry/views/starfish/views/screens/screenLoadSpans')
)}
/>
</Route>
<Route path="app-startup/">
<IndexRoute
component={make(
() => import('sentry/views/starfish/modules/mobile/appStartup')
)}
/>
<Route
path="spans/"
component={make(
() => import('sentry/views/starfish/views/appStartup/screenSummary')
)}
/>
</Route>
</Route>
<Route path="summary/">
<IndexRoute
component={make(
() =>
import('sentry/views/performance/transactionSummary/transactionOverview')
)}
/>
<Route
path="replays/"
component={make(
() => import('sentry/views/performance/transactionSummary/transactionReplays')
)}
/>
<Route
path="vitals/"
component={make(
() => import('sentry/views/performance/transactionSummary/transactionVitals')
)}
/>
<Route
path="tags/"
component={make(
() => import('sentry/views/performance/transactionSummary/transactionTags')
)}
/>
<Route
path="events/"
component={make(
() => import('sentry/views/performance/transactionSummary/transactionEvents')
)}
/>
<Route
path="anomalies/"
component={make(
() =>
import('sentry/views/performance/transactionSummary/transactionAnomalies')
)}
/>
<Route
path="profiles/"
component={make(
() =>
import('sentry/views/performance/transactionSummary/transactionProfiles')
)}
/>
<Route
path="aggregateWaterfall/"
component={make(
() =>
import('sentry/views/performance/transactionSummary/aggregateSpanWaterfall')
)}
/>
<Route path="spans/">
<IndexRoute
component={make(
() => import('sentry/views/performance/transactionSummary/transactionSpans')
)}
/>
<Route
path=":spanSlug/"
component={make(
() =>
import(
'sentry/views/performance/transactionSummary/transactionSpans/spanDetails'
)
)}
/>
</Route>
</Route>
<Route
path="vitaldetail/"
component={make(() => import('sentry/views/performance/vitalDetail'))}
/>
<Route
path="trace/:traceSlug/"
component={make(() => import('sentry/views/performance/traceDetails'))}
/>
<Route
path=":eventSlug/"
component={make(() => import('sentry/views/performance/transactionDetails'))}
/>
</Fragment>
);
const performanceRoutes = (
<Fragment>
{USING_CUSTOMER_DOMAIN && (
<Route
path="/performance/"
component={withDomainRequired(make(() => import('sentry/views/performance')))}
key="orgless-performance-route"
>
{performanceChildRoutes}
</Route>
)}
<Route
path="/organizations/:orgId/performance/"
component={withDomainRedirect(make(() => import('sentry/views/performance')))}
key="org-performance"
>
{performanceChildRoutes}
</Route>
</Fragment>
);
const starfishChildRoutes = (
<Fragment>
<IndexRoute
component={make(() => import('sentry/views/starfish/views/webServiceView'))}
/>
<Route path="endpoint-overview/">
<IndexRoute
component={make(
() => import('sentry/views/starfish/views/webServiceView/endpointOverview')
)}
/>
<Route
path="span/:groupId/"
component={make(() => import('sentry/views/starfish/views/spanSummaryPage'))}
/>
</Route>
<Redirect from="database/" to="/performance/database" />
<Route path="appStartup/">
<IndexRoute
component={make(
() => import('sentry/views/starfish/modules/mobile/appStartup')
)}
/>
<Route
path="spans/"
component={make(
() => import('sentry/views/starfish/views/appStartup/screenSummary')
)}
/>
</Route>
<Route path="responsiveness/">
<IndexRoute
component={make(
() => import('sentry/views/starfish/modules/mobile/responsiveness')
)}
/>
</Route>
<Route path="spans/">
<IndexRoute component={make(() => import('sentry/views/starfish/views/spans'))} />
<Route
path="span/:groupId/"
component={make(() => import('sentry/views/starfish/views/spanSummaryPage'))}
/>
</Route>
</Fragment>
);
const starfishRoutes = (
<Fragment>
{USING_CUSTOMER_DOMAIN && (
<Route
path="/starfish/"
component={withDomainRequired(make(() => import('sentry/views/starfish')))}
key="orgless-starfish-route"
>
{starfishChildRoutes}
</Route>
)}
<Route
path="/organizations/:orgId/starfish/"
component={withDomainRedirect(make(() => import('sentry/views/starfish/')))}
key="org-starfish"
>
{starfishChildRoutes}
</Route>
</Fragment>
);
const userFeedbackRoutes = (
<Fragment>
{USING_CUSTOMER_DOMAIN && (
<Route
path="/user-feedback/"
component={withDomainRequired(make(() => import('sentry/views/userFeedback')))}
key="orgless-user-feedback-route"
/>
)}
<Route
path="/organizations/:orgId/user-feedback/"
component={withDomainRedirect(make(() => import('sentry/views/userFeedback')))}
key="org-user-feedback"
/>
</Fragment>
);
const feedbackChildRoutes = (
<Fragment>
<IndexRoute
component={make(() => import('sentry/views/feedback/feedbackListPage'))}
/>
</Fragment>
);
const feedbackv2Routes = (
<Fragment>
{USING_CUSTOMER_DOMAIN && (
<Route
path="/feedback/"
component={withDomainRequired(
make(() => import('sentry/views/feedback/index'))
)}
key="orgless-feedback-list-route"
>
{feedbackChildRoutes}
</Route>
)}
<Route
path="/organizations/:orgId/feedback/"
component={withDomainRedirect(make(() => import('sentry/views/feedback/index')))}
key="org-feedback-list-route"
>
{feedbackChildRoutes}
</Route>
</Fragment>
);
const issueListRoutes = (
<Fragment>
{USING_CUSTOMER_DOMAIN && (
<Route
path="/issues/(searches/:searchId/)"
component={withDomainRequired(errorHandler(IssueListContainer))}
key="orgless-issues-route"
>
<IndexRoute component={errorHandler(IssueListOverview)} />
</Route>
)}
<Route
path="/organizations/:orgId/issues/(searches/:searchId/)"
component={withDomainRedirect(errorHandler(IssueListContainer))}
key="org-issues"
>
<Redirect from="/organizations/:orgId/" to="/organizations/:orgId/issues/" />
<IndexRoute component={errorHandler(IssueListOverview)} />
</Route>
</Fragment>
);
// Once org issues is complete, these routes can be nested under
// /organizations/:orgId/issues
const issueTabs = ({forCustomerDomain}: {forCustomerDomain: boolean}) => {
const hoc = forCustomerDomain ? withDomainRequired : x => x;
return (
<Fragment>
<IndexRoute
component={hoc(
make(() => import('sentry/views/issueDetails/groupEventDetails'))
)}
/>
<Route
path={TabPaths[Tab.REPLAYS]}
component={hoc(make(() => import('sentry/views/issueDetails/groupReplays')))}
/>
<Route
path={TabPaths[Tab.ACTIVITY]}
component={hoc(make(() => import('sentry/views/issueDetails/groupActivity')))}
/>
<Route
path={TabPaths[Tab.EVENTS]}
component={hoc(make(() => import('sentry/views/issueDetails/groupEvents')))}
/>
<Route
path={TabPaths[Tab.TAGS]}
component={hoc(make(() => import('sentry/views/issueDetails/groupTags')))}
/>
<Route
path={`${TabPaths[Tab.TAGS]}:tagKey/`}
component={make(() => import('sentry/views/issueDetails/groupTagValues'))}
/>
<Route
path={TabPaths[Tab.USER_FEEDBACK]}
component={hoc(
make(() => import('sentry/views/issueDetails/groupUserFeedback'))
)}
/>
<Route
path={TabPaths[Tab.ATTACHMENTS]}
component={hoc(
make(() => import('sentry/views/issueDetails/groupEventAttachments'))
)}
/>
<Route
path={TabPaths[Tab.SIMILAR_ISSUES]}
component={hoc(
make(() => import('sentry/views/issueDetails/groupSimilarIssues'))
)}
/>
<Route
path={TabPaths[Tab.MERGED]}
component={hoc(make(() => import('sentry/views/issueDetails/groupMerged')))}
/>
</Fragment>
);
};
const issueDetailsChildRoutes = ({forCustomerDomain}: {forCustomerDomain: boolean}) => (
<Fragment>
{issueTabs({forCustomerDomain})}
<Route path={`${TabPaths[Tab.EVENTS]}:eventId/`}>
{issueTabs({forCustomerDomain})}
</Route>
</Fragment>
);
const issueDetailsRoutes = (
<Fragment>
<Route
path="/organizations/:orgId/issues/:groupId/"
component={withDomainRedirect(
make(() => import('sentry/views/issueDetails/groupDetails'))
)}
key="org-issues-group-id"
>
{issueDetailsChildRoutes({forCustomerDomain: false})}
</Route>
{USING_CUSTOMER_DOMAIN && (
<Route
path="/issues/:groupId/"
component={withDomainRequired(
make(() => import('sentry/views/issueDetails/groupDetails'))
)}
key="orgless-issues-group-id-route"
>
{issueDetailsChildRoutes({forCustomerDomain: true})}
</Route>
)}
</Fragment>
);
// These are the "manage" pages. For sentry.io, these are _different_ from
// the SaaS admin routes in getsentry.
const adminManageRoutes = (
<Route
path="/manage/"
component={make(() => import('sentry/views/admin/adminLayout'))}
>
<IndexRoute component={make(() => import('sentry/views/admin/adminOverview'))} />
<Route
path="buffer/"
component={make(() => import('sentry/views/admin/adminBuffer'))}
/>
<Route
path="relays/"
component={make(() => import('sentry/views/admin/adminRelays'))}
/>
<Route
path="organizations/"
component={make(() => import('sentry/views/admin/adminOrganizations'))}
/>
<Route
path="projects/"
component={make(() => import('sentry/views/admin/adminProjects'))}
/>
<Route
path="queue/"
component={make(() => import('sentry/views/admin/adminQueue'))}
/>
<Route
path="quotas/"
component={make(() => import('sentry/views/admin/adminQuotas'))}
/>
<Route
path="settings/"
component={make(() => import('sentry/views/admin/adminSettings'))}
/>
<Route path="users/">
<IndexRoute component={make(() => import('sentry/views/admin/adminUsers'))} />
<Route
path=":id"
component={make(() => import('sentry/views/admin/adminUserEdit'))}
/>
</Route>
<Route
path="status/mail/"
component={make(() => import('sentry/views/admin/adminMail'))}
/>
<Route
path="status/environment/"
component={make(() => import('sentry/views/admin/adminEnvironment'))}
/>
<Route
path="status/packages/"
component={make(() => import('sentry/views/admin/adminPackages'))}
/>
<Route
path="status/warnings/"
component={make(() => import('sentry/views/admin/adminWarnings'))}
/>
</Route>
);
// XXX(epurkhiser): This should probably go away. It's not totally clear to
// me why we need the OrganizationRoot root container.
const legacyOrganizationRootRoutes = (
<Route component={errorHandler(OrganizationRoot)}>
<Redirect from="/organizations/:orgId/teams/new/" to="/settings/:orgId/teams/" />
<Route path="/organizations/:orgId/">
{hook('routes:organization')}
<Redirect from="/organizations/:orgId/teams/" to="/settings/:orgId/teams/" />
<Redirect
from="/organizations/:orgId/teams/your-teams/"
to="/settings/:orgId/teams/"
/>
<Redirect
from="/organizations/:orgId/teams/all-teams/"
to="/settings/:orgId/teams/"
/>
<Redirect
from="/organizations/:orgId/teams/:teamId/"
to="/settings/:orgId/teams/:teamId/"
/>
<Redirect
from="/organizations/:orgId/teams/:teamId/members/"
to="/settings/:orgId/teams/:teamId/members/"
/>
<Redirect
from="/organizations/:orgId/teams/:teamId/projects/"
to="/settings/:orgId/teams/:teamId/projects/"
/>
<Redirect
from="/organizations/:orgId/teams/:teamId/settings/"
to="/settings/:orgId/teams/:teamId/settings/"
/>
<Redirect from="/organizations/:orgId/settings/" to="/settings/:orgId/" />
<Redirect
from="/organizations/:orgId/api-keys/"
to="/settings/:orgId/api-keys/"
/>
<Redirect
from="/organizations/:orgId/api-keys/:apiKey/"
to="/settings/:orgId/api-keys/:apiKey/"
/>
<Redirect from="/organizations/:orgId/members/" to="/settings/:orgId/members/" />
<Redirect
from="/organizations/:orgId/members/:memberId/"
to="/settings/:orgId/members/:memberId/"
/>
<Redirect
from="/organizations/:orgId/rate-limits/"
to="/settings/:orgId/rate-limits/"
/>
<Redirect from="/organizations/:orgId/repos/" to="/settings/:orgId/repos/" />
</Route>
</Route>
);
const gettingStartedRoutes = (
<Fragment>
{USING_CUSTOMER_DOMAIN && (
<Fragment>
<Redirect
from="/getting-started/:projectId/"
to="/projects/:projectId/getting-started/"
/>
<Redirect
from="/getting-started/:projectId/:platform/"
to="/projects/:projectId/getting-started/"
/>
</Fragment>
)}
<Redirect
from="/:orgId/:projectId/getting-started/"
to="/organizations/:orgId/projects/:projectId/getting-started/"
/>
<Redirect
from="/:orgId/:projectId/getting-started/:platform/"
to="/organizations/:orgId/projects/:projectId/getting-started/"
/>
</Fragment>
);
const profilingChildRoutes = (
<Fragment>
<IndexRoute component={make(() => import('sentry/views/profiling/content'))} />
<Route
path="summary/:projectId/"
component={make(() => import('sentry/views/profiling/profileSummary'))}
/>
<Route
path="profile/:projectId/differential-flamegraph/"
component={make(() => import('sentry/views/profiling/differentialFlamegraph'))}
/>
<Route
path="profile/:projectId/:eventId/"
component={make(() => import('sentry/views/profiling/profilesProvider'))}
>
<Route
path="flamegraph/"
component={make(() => import('sentry/views/profiling/profileFlamechart'))}
/>
</Route>
</Fragment>
);
const profilingRoutes = (
<Fragment>
{USING_CUSTOMER_DOMAIN && (
<Route
path="/profiling/"
component={withDomainRequired(make(() => import('sentry/views/profiling')))}
key="orgless-profiling-route"
>
{profilingChildRoutes}
</Route>
)}
<Route
path="/organizations/:orgId/profiling/"
component={withDomainRedirect(make(() => import('sentry/views/profiling')))}
key="org-profiling"
>
{profilingChildRoutes}
</Route>
</Fragment>
);
const ddmChildRoutes = (
<Fragment>
<IndexRoute component={make(() => import('sentry/views/ddm/ddm'))} />
</Fragment>
);
const ddmRoutes = (
<Fragment>
{USING_CUSTOMER_DOMAIN && (
<Route
path="/ddm/"
component={withDomainRequired(make(() => import('sentry/views/ddm')))}
key="orgless-ddm-route"
>
{ddmChildRoutes}
</Route>
)}
<Route
path="/organizations/:orgId/ddm/"
component={withDomainRedirect(make(() => import('sentry/views/ddm')))}
key="org-ddm"
>
{ddmChildRoutes}
</Route>
</Fragment>
);
// Support for deprecated URLs (pre-Sentry 10). We just redirect users to new
// canonical URLs.
//
// XXX(epurkhiser): Can these be moved over to the legacyOrgRedirects routes,
// or do these need to be nested into the OrganizationLayout tree?
const legacyOrgRedirects = (
<Route path="/:orgId/:projectId/">
<IndexRoute
component={errorHandler(
redirectDeprecatedProjectRoute(
({orgId, projectId}) => `/organizations/${orgId}/issues/?project=${projectId}`
)
)}
/>
<Route
path="issues/"
component={errorHandler(
redirectDeprecatedProjectRoute(
({orgId, projectId}) => `/organizations/${orgId}/issues/?project=${projectId}`
)
)}
/>
<Route
path="dashboard/"
component={errorHandler(
redirectDeprecatedProjectRoute(
({orgId, projectId}) =>
`/organizations/${orgId}/dashboards/?project=${projectId}`
)
)}
/>
<Route
path="user-feedback/"
component={errorHandler(
redirectDeprecatedProjectRoute(
({orgId, projectId}) =>
`/organizations/${orgId}/user-feedback/?project=${projectId}`
)
)}
/>
<Route
path="releases/"
component={errorHandler(
redirectDeprecatedProjectRoute(
({orgId, projectId}) =>
`/organizations/${orgId}/releases/?project=${projectId}`
)
)}
/>
<Route
path="releases/:version/"
component={errorHandler(
redirectDeprecatedProjectRoute(
({orgId, projectId, router}) =>
`/organizations/${orgId}/releases/${router.params.version}/?project=${projectId}`
)
)}
/>
<Route
path="releases/:version/new-events/"
component={errorHandler(
redirectDeprecatedProjectRoute(
({orgId, projectId, router}) =>
`/organizations/${orgId}/releases/${router.params.version}/new-events/?project=${projectId}`
)
)}
/>
<Route
path="releases/:version/all-events/"
component={errorHandler(
redirectDeprecatedProjectRoute(
({orgId, projectId, router}) =>
`/organizations/${orgId}/releases/${router.params.version}/all-events/?project=${projectId}`
)
)}
/>
<Route
path="releases/:version/commits/"
component={errorHandler(
redirectDeprecatedProjectRoute(
({orgId, projectId, router}) =>
`/organizations/${orgId}/releases/${router.params.version}/commits/?project=${projectId}`
)
)}
/>
</Route>
);
const organizationRoutes = (
<Route component={errorHandler(OrganizationLayout)}>
{settingsRoutes}
{projectsRoutes}
{dashboardRoutes}
{userFeedbackRoutes}
{feedbackv2Routes}
{issueListRoutes}
{issueDetailsRoutes}
{alertRoutes}
{cronsRoutes}
{replayRoutes}
{releasesRoutes}
{releaseThresholdRoutes}
{activityRoutes}
{statsRoutes}
{discoverRoutes}
{performanceRoutes}
{starfishRoutes}
{profilingRoutes}
{ddmRoutes}
{gettingStartedRoutes}
{adminManageRoutes}
{legacyOrganizationRootRoutes}
{legacyOrgRedirects}
</Route>
);
const legacyRedirectRoutes = (
<Route path="/:orgId/">
<IndexRedirect to="/organizations/:orgId/" />
<Route path=":projectId/settings/">
<Redirect from="teams/" to="/settings/:orgId/projects/:projectId/teams/" />
<Redirect from="alerts/" to="/settings/:orgId/projects/:projectId/alerts/" />
<Redirect
from="alerts/rules/"
to="/settings/:orgId/projects/:projectId/alerts/rules/"
/>
<Redirect
from="alerts/rules/new/"
to="/settings/:orgId/projects/:projectId/alerts/rules/new/"
/>
<Redirect
from="alerts/rules/:ruleId/"
to="/settings/:orgId/projects/:projectId/alerts/rules/:ruleId/"
/>
<Redirect
from="environments/"
to="/settings/:orgId/projects/:projectId/environments/"
/>
<Redirect
from="environments/hidden/"
to="/settings/:orgId/projects/:projectId/environments/hidden/"
/>
<Redirect from="tags/" to="/settings/projects/:orgId/projects/:projectId/tags/" />
<Redirect
from="issue-tracking/"
to="/settings/:orgId/projects/:projectId/issue-tracking/"
/>
<Redirect
from="release-tracking/"
to="/settings/:orgId/projects/:projectId/release-tracking/"
/>
<Redirect
from="ownership/"
to="/settings/:orgId/projects/:projectId/ownership/"
/>
<Redirect
from="data-forwarding/"
to="/settings/:orgId/projects/:projectId/data-forwarding/"
/>
<Redirect
from="debug-symbols/"
to="/settings/:orgId/projects/:projectId/debug-symbols/"
/>
<Redirect
from="processing-issues/"
to="/settings/:orgId/projects/:projectId/processing-issues/"
/>
<Redirect from="filters/" to="/settings/:orgId/projects/:projectId/filters/" />
<Redirect from="hooks/" to="/settings/:orgId/projects/:projectId/hooks/" />
<Redirect from="keys/" to="/settings/:orgId/projects/:projectId/keys/" />
<Redirect
from="keys/:keyId/"
to="/settings/:orgId/projects/:projectId/keys/:keyId/"
/>
<Redirect
from="user-feedback/"
to="/settings/:orgId/projects/:projectId/user-feedback/"
/>
<Redirect
from="security-headers/"
to="/settings/:orgId/projects/:projectId/security-headers/"
/>
<Redirect
from="security-headers/csp/"
to="/settings/:orgId/projects/:projectId/security-headers/csp/"
/>
<Redirect
from="security-headers/expect-ct/"
to="/settings/:orgId/projects/:projectId/security-headers/expect-ct/"
/>
<Redirect
from="security-headers/hpkp/"
to="/settings/:orgId/projects/:projectId/security-headers/hpkp/"
/>
<Redirect from="plugins/" to="/settings/:orgId/projects/:projectId/plugins/" />
<Redirect
from="plugins/:pluginId/"
to="/settings/:orgId/projects/:projectId/plugins/:pluginId/"
/>
<Redirect
from="integrations/:providerKey/"
to="/settings/:orgId/projects/:projectId/integrations/:providerKey/"
/>
</Route>
<Redirect from=":projectId/group/:groupId/" to="issues/:groupId/" />
<Redirect
from=":projectId/issues/:groupId/"
to="/organizations/:orgId/issues/:groupId/"
/>
<Redirect
from=":projectId/issues/:groupId/events/"
to="/organizations/:orgId/issues/:groupId/events/"
/>
<Redirect
from=":projectId/issues/:groupId/events/:eventId/"
to="/organizations/:orgId/issues/:groupId/events/:eventId/"
/>
<Redirect
from=":projectId/issues/:groupId/tags/"
to="/organizations/:orgId/issues/:groupId/tags/"
/>
<Redirect
from=":projectId/issues/:groupId/tags/:tagKey/"
to="/organizations/:orgId/issues/:groupId/tags/:tagKey/"
/>
<Redirect
from=":projectId/issues/:groupId/feedback/"
to="/organizations/:orgId/issues/:groupId/feedback/"
/>
<Redirect
from=":projectId/issues/:groupId/similar/"
to="/organizations/:orgId/issues/:groupId/similar/"
/>
<Redirect
from=":projectId/issues/:groupId/merged/"
to="/organizations/:orgId/issues/:groupId/merged/"
/>
<Route
path=":projectId/events/:eventId/"
component={errorHandler(ProjectEventRedirect)}
/>
</Route>
);
const appRoutes = (
<Route>
{experimentalSpaRoutes}
<Route path="/" component={errorHandler(App)}>
{rootRoutes}
{organizationRoutes}
{legacyRedirectRoutes}
<Route path="*" component={errorHandler(RouteNotFound)} />
</Route>
</Route>
);
return appRoutes;
}
// We load routes both when initializing the SDK (for routing integrations) and
// when the app renders Main. Memoize to avoid rebuilding the route tree.
export const routes = memoize(buildRoutes);
// Exported for use in tests.
export {buildRoutes};
function NoOp({children}: {children: JSX.Element}) {
return children;
}