You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

startReactApp.tsx 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2021 SonarSource SA
  4. * mailto:info AT sonarsource DOT com
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 3 of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program; if not, write to the Free Software Foundation,
  18. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. */
  20. /* eslint-disable react/jsx-sort-props */
  21. import { Location } from 'history';
  22. import { pick } from 'lodash';
  23. import * as React from 'react';
  24. import ReactDom, { render } from 'react-dom';
  25. import { HelmetProvider } from 'react-helmet-async';
  26. import { IntlProvider } from 'react-intl';
  27. import { Provider } from 'react-redux';
  28. import { IndexRoute, Redirect, Route, RouteConfig, RouteProps, Router } from 'react-router';
  29. import accountRoutes from '../../apps/account/routes';
  30. import applicationConsoleRoutes from '../../apps/application-console/routes';
  31. import applicationSettingsRoutes from '../../apps/application-settings/routes';
  32. import auditLogsRoutes from '../../apps/audit-logs/routes';
  33. import backgroundTasksRoutes from '../../apps/background-tasks/routes';
  34. import codeRoutes from '../../apps/code/routes';
  35. import codingRulesRoutes from '../../apps/coding-rules/routes';
  36. import componentMeasuresRoutes from '../../apps/component-measures/routes';
  37. import documentationRoutes from '../../apps/documentation/routes';
  38. import groupsRoutes from '../../apps/groups/routes';
  39. import Issues from '../../apps/issues/components/AppContainer';
  40. import { maintenanceRoutes, setupRoutes } from '../../apps/maintenance/routes';
  41. import marketplaceRoutes from '../../apps/marketplace/routes';
  42. import overviewRoutes from '../../apps/overview/routes';
  43. import permissionTemplatesRoutes from '../../apps/permission-templates/routes';
  44. import { globalPermissionsRoutes, projectPermissionsRoutes } from '../../apps/permissions/routes';
  45. import portfolioRoutes from '../../apps/portfolio/routes';
  46. import projectActivityRoutes from '../../apps/projectActivity/routes';
  47. import projectBaselineRoutes from '../../apps/projectBaseline/routes';
  48. import projectBranchesRoutes from '../../apps/projectBranches/routes';
  49. import projectDumpRoutes from '../../apps/projectDump/routes';
  50. import projectQualityGateRoutes from '../../apps/projectQualityGate/routes';
  51. import projectQualityProfilesRoutes from '../../apps/projectQualityProfiles/routes';
  52. import projectsRoutes from '../../apps/projects/routes';
  53. import projectsManagementRoutes from '../../apps/projectsManagement/routes';
  54. import qualityGatesRoutes from '../../apps/quality-gates/routes';
  55. import qualityProfilesRoutes from '../../apps/quality-profiles/routes';
  56. import sessionsRoutes from '../../apps/sessions/routes';
  57. import settingsRoutes from '../../apps/settings/routes';
  58. import systemRoutes from '../../apps/system/routes';
  59. import tutorialsRoutes from '../../apps/tutorials/routes';
  60. import usersRoutes from '../../apps/users/routes';
  61. import webAPIRoutes from '../../apps/web-api/routes';
  62. import webhooksRoutes from '../../apps/webhooks/routes';
  63. import withIndexationGuard from '../../components/hoc/withIndexationGuard';
  64. import { lazyLoadComponent } from '../../components/lazyLoadComponent';
  65. import getHistory from '../../helpers/getHistory';
  66. import App from '../components/App';
  67. import GlobalContainer from '../components/GlobalContainer';
  68. import { PageContext } from '../components/indexation/PageUnavailableDueToIndexation';
  69. import MigrationContainer from '../components/MigrationContainer';
  70. import NonAdminPagesContainer from '../components/NonAdminPagesContainer';
  71. import getStore from './getStore';
  72. /*
  73. * Expose dependencies to extensions
  74. */
  75. function attachToGlobal() {
  76. window.React = React;
  77. window.ReactDOM = ReactDom;
  78. }
  79. function handleUpdate(this: { state: { location: Location } }) {
  80. const { action } = this.state.location;
  81. if (action === 'PUSH') {
  82. window.scrollTo(0, 0);
  83. }
  84. }
  85. // this is not an official api
  86. const RouteWithChildRoutes = Route as React.ComponentClass<
  87. RouteProps & { childRoutes: RouteConfig }
  88. >;
  89. function renderRedirects() {
  90. return (
  91. <>
  92. <Route
  93. path="/account/issues"
  94. onEnter={(_, replace) => {
  95. replace({ pathname: '/issues', query: { myIssues: 'true', resolved: 'false' } });
  96. }}
  97. />
  98. <Route
  99. path="/codingrules"
  100. onEnter={(_, replace) => {
  101. replace(`/coding_rules${window.location.hash}`);
  102. }}
  103. />
  104. <Route
  105. path="/dashboard/index/:key"
  106. onEnter={(nextState, replace) => {
  107. replace({ pathname: '/dashboard', query: { id: nextState.params.key } });
  108. }}
  109. />
  110. <Route
  111. path="/issues/search"
  112. onEnter={(_, replace) => {
  113. replace(`/issues${window.location.hash}`);
  114. }}
  115. />
  116. <Redirect from="/admin" to="/admin/settings" />
  117. <Redirect from="/background_tasks" to="/admin/background_tasks" />
  118. <Redirect from="/component/index" to="/component" />
  119. <Redirect from="/component_issues" to="/project/issues" />
  120. <Redirect from="/dashboard/index" to="/dashboard" />
  121. <Redirect
  122. from="/documentation/analysis/languages/vb"
  123. to="/documentation/analysis/languages/vbnet/"
  124. />
  125. <Redirect from="/governance" to="/portfolio" />
  126. <Redirect from="/groups" to="/admin/groups" />
  127. <Redirect from="/extension/governance/portfolios" to="/portfolios" />
  128. <Redirect from="/permission_templates" to="/admin/permission_templates" />
  129. <Redirect from="/profiles/index" to="/profiles" />
  130. <Redirect from="/projects_admin" to="/admin/projects_management" />
  131. <Redirect from="/quality_gates/index" to="/quality_gates" />
  132. <Redirect from="/roles/global" to="/admin/permissions" />
  133. <Redirect from="/admin/roles/global" to="/admin/permissions" />
  134. <Redirect from="/settings" to="/admin/settings" />
  135. <Redirect from="/settings/encryption" to="/admin/settings/encryption" />
  136. <Redirect from="/settings/index" to="/admin/settings" />
  137. <Redirect from="/sessions/login" to="/sessions/new" />
  138. <Redirect from="/system" to="/admin/system" />
  139. <Redirect from="/system/index" to="/admin/system" />
  140. <Redirect from="/view" to="/portfolio" />
  141. <Redirect from="/users" to="/admin/users" />
  142. <Redirect from="/onboarding" to="/projects/create" />
  143. <Redirect from="markdown/help" to="formatting/help" />
  144. </>
  145. );
  146. }
  147. function renderComponentRoutes() {
  148. return (
  149. <Route component={lazyLoadComponent(() => import('../components/ComponentContainer'))}>
  150. {/* This container is a catch-all for all non-admin pages */}
  151. <Route component={NonAdminPagesContainer}>
  152. <RouteWithChildRoutes path="code" childRoutes={codeRoutes} />
  153. <RouteWithChildRoutes path="component_measures" childRoutes={componentMeasuresRoutes} />
  154. <RouteWithChildRoutes path="dashboard" childRoutes={overviewRoutes} />
  155. <RouteWithChildRoutes path="portfolio" childRoutes={portfolioRoutes} />
  156. <RouteWithChildRoutes path="project/activity" childRoutes={projectActivityRoutes} />
  157. <Route
  158. path="project/extension/:pluginKey/:extensionKey"
  159. component={lazyLoadComponent(() =>
  160. import('../components/extensions/ProjectPageExtension')
  161. )}
  162. />
  163. <Route
  164. path="project/issues"
  165. component={Issues}
  166. onEnter={({ location: { query } }, replace) => {
  167. if (query.types) {
  168. if (query.types === 'SECURITY_HOTSPOT') {
  169. replace({
  170. pathname: '/security_hotspots',
  171. query: { ...pick(query, ['id', 'branch', 'pullRequest']), assignedToMe: false }
  172. });
  173. } else {
  174. query.types = query.types
  175. .split(',')
  176. .filter((type: string) => type !== 'SECURITY_HOTSPOT')
  177. .join(',');
  178. }
  179. }
  180. }}
  181. />
  182. <Route
  183. path="security_hotspots"
  184. component={lazyLoadComponent(() =>
  185. import('../../apps/security-hotspots/SecurityHotspotsApp')
  186. )}
  187. />
  188. <RouteWithChildRoutes path="project/quality_gate" childRoutes={projectQualityGateRoutes} />
  189. <RouteWithChildRoutes
  190. path="project/quality_profiles"
  191. childRoutes={projectQualityProfilesRoutes}
  192. />
  193. <RouteWithChildRoutes path="tutorials" childRoutes={tutorialsRoutes} />
  194. </Route>
  195. <Route component={lazyLoadComponent(() => import('../components/ProjectAdminContainer'))}>
  196. <Route
  197. path="project/admin/extension/:pluginKey/:extensionKey"
  198. component={lazyLoadComponent(() =>
  199. import('../components/extensions/ProjectAdminPageExtension')
  200. )}
  201. />
  202. <RouteWithChildRoutes path="project/background_tasks" childRoutes={backgroundTasksRoutes} />
  203. <RouteWithChildRoutes path="project/baseline" childRoutes={projectBaselineRoutes} />
  204. <RouteWithChildRoutes path="project/branches" childRoutes={projectBranchesRoutes} />
  205. <RouteWithChildRoutes path="project/import_export" childRoutes={projectDumpRoutes} />
  206. <RouteWithChildRoutes path="project/settings" childRoutes={settingsRoutes} />
  207. <RouteWithChildRoutes path="project_roles" childRoutes={projectPermissionsRoutes} />
  208. <RouteWithChildRoutes path="application/console" childRoutes={applicationConsoleRoutes} />
  209. <RouteWithChildRoutes path="application/settings" childRoutes={applicationSettingsRoutes} />
  210. <RouteWithChildRoutes path="project/webhooks" childRoutes={webhooksRoutes} />
  211. <Route
  212. path="project/deletion"
  213. component={lazyLoadComponent(() => import('../../apps/projectDeletion/App'))}
  214. />
  215. <Route
  216. path="project/links"
  217. component={lazyLoadComponent(() => import('../../apps/projectLinks/App'))}
  218. />
  219. <Route
  220. path="project/key"
  221. component={lazyLoadComponent(() => import('../../apps/projectKey/Key'))}
  222. />
  223. </Route>
  224. </Route>
  225. );
  226. }
  227. function renderAdminRoutes() {
  228. return (
  229. <Route component={lazyLoadComponent(() => import('../components/AdminContainer'))} path="admin">
  230. <Route
  231. path="extension/:pluginKey/:extensionKey"
  232. component={lazyLoadComponent(() =>
  233. import('../components/extensions/GlobalAdminPageExtension')
  234. )}
  235. />
  236. <RouteWithChildRoutes path="audit" childRoutes={auditLogsRoutes} />
  237. <RouteWithChildRoutes path="background_tasks" childRoutes={backgroundTasksRoutes} />
  238. <RouteWithChildRoutes path="groups" childRoutes={groupsRoutes} />
  239. <RouteWithChildRoutes path="permission_templates" childRoutes={permissionTemplatesRoutes} />
  240. <RouteWithChildRoutes path="permissions" childRoutes={globalPermissionsRoutes} />
  241. <RouteWithChildRoutes path="projects_management" childRoutes={projectsManagementRoutes} />
  242. <RouteWithChildRoutes path="settings" childRoutes={settingsRoutes} />
  243. <RouteWithChildRoutes path="system" childRoutes={systemRoutes} />
  244. <RouteWithChildRoutes path="marketplace" childRoutes={marketplaceRoutes} />
  245. <RouteWithChildRoutes path="users" childRoutes={usersRoutes} />
  246. <RouteWithChildRoutes path="webhooks" childRoutes={webhooksRoutes} />
  247. </Route>
  248. );
  249. }
  250. export default function startReactApp(
  251. lang: string,
  252. currentUser?: T.CurrentUser,
  253. appState?: T.AppState
  254. ) {
  255. attachToGlobal();
  256. const el = document.getElementById('content');
  257. const history = getHistory();
  258. const store = getStore(currentUser, appState);
  259. render(
  260. <HelmetProvider>
  261. <Provider store={store}>
  262. <IntlProvider defaultLocale={lang} locale={lang}>
  263. <Router history={history} onUpdate={handleUpdate}>
  264. {renderRedirects()}
  265. <Route
  266. path="formatting/help"
  267. component={lazyLoadComponent(() => import('../components/FormattingHelp'))}
  268. />
  269. <Route component={lazyLoadComponent(() => import('../components/SimpleContainer'))}>
  270. <Route path="maintenance">{maintenanceRoutes}</Route>
  271. <Route path="setup">{setupRoutes}</Route>
  272. </Route>
  273. <Route component={MigrationContainer}>
  274. <Route
  275. component={lazyLoadComponent(() =>
  276. import('../components/SimpleSessionsContainer')
  277. )}>
  278. <RouteWithChildRoutes path="/sessions" childRoutes={sessionsRoutes} />
  279. </Route>
  280. <Route path="/" component={App}>
  281. <IndexRoute component={lazyLoadComponent(() => import('../components/Landing'))} />
  282. <Route component={GlobalContainer}>
  283. <RouteWithChildRoutes path="account" childRoutes={accountRoutes} />
  284. <RouteWithChildRoutes path="coding_rules" childRoutes={codingRulesRoutes} />
  285. <RouteWithChildRoutes path="documentation" childRoutes={documentationRoutes} />
  286. <Route
  287. path="extension/:pluginKey/:extensionKey"
  288. component={lazyLoadComponent(() =>
  289. import('../components/extensions/GlobalPageExtension')
  290. )}
  291. />
  292. <Route
  293. path="issues"
  294. component={withIndexationGuard(Issues, PageContext.Issues)}
  295. />
  296. <RouteWithChildRoutes path="projects" childRoutes={projectsRoutes} />
  297. <RouteWithChildRoutes path="quality_gates" childRoutes={qualityGatesRoutes} />
  298. <Route
  299. path="portfolios"
  300. component={lazyLoadComponent(() =>
  301. import('../components/extensions/PortfoliosPage')
  302. )}
  303. />
  304. <RouteWithChildRoutes path="profiles" childRoutes={qualityProfilesRoutes} />
  305. <RouteWithChildRoutes path="web_api" childRoutes={webAPIRoutes} />
  306. {renderComponentRoutes()}
  307. {renderAdminRoutes()}
  308. </Route>
  309. <Route
  310. // We don't want this route to have any menu.
  311. // That is why we can not have it under the accountRoutes
  312. path="account/reset_password"
  313. component={lazyLoadComponent(() => import('../components/ResetPassword'))}
  314. />
  315. <Route
  316. // We don't want this route to have any menu. This is why we define it here
  317. // rather than under the admin routes.
  318. path="admin/change_admin_password"
  319. component={lazyLoadComponent(() =>
  320. import('../../apps/change-admin-password/ChangeAdminPasswordApp')
  321. )}
  322. />
  323. <Route
  324. // We don't want this route to have any menu. This is why we define it here
  325. // rather than under the admin routes.
  326. path="admin/plugin_risk_consent"
  327. component={lazyLoadComponent(() => import('../components/PluginRiskConsent'))}
  328. />
  329. <Route
  330. path="not_found"
  331. component={lazyLoadComponent(() => import('../components/NotFound'))}
  332. />
  333. <Route
  334. path="*"
  335. component={lazyLoadComponent(() => import('../components/NotFound'))}
  336. />
  337. </Route>
  338. </Route>
  339. </Router>
  340. </IntlProvider>
  341. </Provider>
  342. </HelmetProvider>,
  343. el
  344. );
  345. }