import type {RouteComponent, RouteComponentProps} from 'react-router'; import {formatPattern} from 'react-router'; import trimEnd from 'lodash/trimEnd'; import trimStart from 'lodash/trimStart'; import Redirect from 'sentry/components/redirect'; import recreateRoute from 'sentry/utils/recreateRoute'; import {normalizeUrl} from 'sentry/utils/withDomainRequired'; import useOrganization from './useOrganization'; /** * withDomainRedirect is a higher-order component (HOC) meant to be used with components within * static/app/routes.tsx whose route paths contains the :orgId parameter at least once. * For example: * * * * withDomainRedirect checks if the associated route path is accessed within a customer domain (e.g. orgslug.sentry.io), * then redirect the browser to a route path with /organization/:orgId/ and /:orgId/ omitted (in that order). * For example, redirect /organizations/:orgId/issues/(searches/:searchId/) to /issues/(searches/:searchId/) * * If a redirect should occur, then a Sentry event will be emitted. * * If either a customer domain is not being used, or if :orgId is not present in the route path, then WrappedComponent * is rendered. */ function withDomainRedirect

>( WrappedComponent: RouteComponent ) { return function WithDomainRedirectWrapper(props: P) { const {customerDomain, links} = window.__initialData; const {sentryUrl} = links; const currentOrganization = useOrganization({allowNull: true}); if (customerDomain) { // Customer domain is being used on a route that has an :orgId parameter. const redirectPath = `${window.location.pathname}${window.location.search}${window.location.hash}`; const redirectURL = `${trimEnd(sentryUrl, '/')}/${trimStart(redirectPath, '/')}`; // If we have domain information, but the subdomain and slug are different // redirect to the slug path and let django decide what happens next. if ( currentOrganization && customerDomain.subdomain && (currentOrganization.slug !== customerDomain.subdomain || !currentOrganization.features.includes('customer-domains')) ) { window.location.replace(redirectURL); return null; } const {params, routes} = props; // Regenerate the full route with the :orgId parameter omitted. const newParams = {...params}; Object.keys(params).forEach(param => { newParams[param] = `:${param}`; }); const fullRoute = recreateRoute('', {routes, params: newParams}); const orglessSlugRoute = normalizeUrl(fullRoute, {forceCustomerDomain: true}); if (orglessSlugRoute === fullRoute) { // :orgId is not present in the route, so we do not need to perform a redirect here. return ; } const orglessRedirectPath = formatPattern(orglessSlugRoute, params); const redirectOrgURL = `/${trimStart(orglessRedirectPath, '/')}${ window.location.search }${window.location.hash}`; // Redirect to a route path with :orgId omitted. return ; } return ; }; } export default withDomainRedirect;